diff --git a/pkNX.Containers/GARC/GARC.cs b/pkNX.Containers/GARC/GARC.cs index 51337736..8592f6c1 100644 --- a/pkNX.Containers/GARC/GARC.cs +++ b/pkNX.Containers/GARC/GARC.cs @@ -127,7 +127,7 @@ public override void Dump(string path, ContainerHandler handler) br.BaseStream.Position = 10; var ver = br.ReadUInt16(); - if (ver == 0x0600 || ver == 0x0400) + if (ver is 0x0600 or 0x0400) return new GARC(br); return null; } diff --git a/pkNX.Containers/GARC/GARCHeader.cs b/pkNX.Containers/GARC/GARCHeader.cs index c842f8f4..376e15b1 100644 --- a/pkNX.Containers/GARC/GARCHeader.cs +++ b/pkNX.Containers/GARC/GARCHeader.cs @@ -27,12 +27,12 @@ public class GARCHeader public GARCHeader(GARCVersion version) { - if (version == GARCVersion.VER_6) - Version = VER_6; - else if (version == GARCVersion.VER_4) - Version = VER_4; - else - Version = (ushort)version; + Version = version switch + { + GARCVersion.VER_6 => VER_6, + GARCVersion.VER_4 => VER_4, + _ => (ushort)version, + }; HeaderSize = VER6 ? 0x24 : 0x1C; } diff --git a/pkNX.Containers/LargeContainerEntry.cs b/pkNX.Containers/LargeContainerEntry.cs index aa9a7624..55bbbbbe 100644 --- a/pkNX.Containers/LargeContainerEntry.cs +++ b/pkNX.Containers/LargeContainerEntry.cs @@ -14,7 +14,7 @@ public byte[] GetFileData(Stream parent) { parent.Seek(Start + ParentDataPosition, SeekOrigin.Begin); byte[] data = new byte[Length]; - parent.Read(data, 0, Length); + _ = parent.Read(data, 0, Length); return data; } diff --git a/pkNX.Containers/Mini/Mini.cs b/pkNX.Containers/Mini/Mini.cs index e8552a7e..71d561cf 100644 --- a/pkNX.Containers/Mini/Mini.cs +++ b/pkNX.Containers/Mini/Mini.cs @@ -47,14 +47,11 @@ public void CancelEdits() Files[i] = (byte[]) Backup[i].Clone(); } - public Task SaveAs(string path, ContainerHandler handler, CancellationToken token) + public Task SaveAs(string path, ContainerHandler handler, CancellationToken token) => new(() => { - return new(() => - { - byte[] data = MiniUtil.PackMini(Files, Identifier); - FileMitm.WriteAllBytes(path, data); - }, token); - } + byte[] data = MiniUtil.PackMini(Files, Identifier); + FileMitm.WriteAllBytes(path, data); + }, token); public void Dump(string path, ContainerHandler handler) { diff --git a/pkNX.Containers/Misc/FnvHash.cs b/pkNX.Containers/Misc/FnvHash.cs index a5202cd6..12cf267d 100644 --- a/pkNX.Containers/Misc/FnvHash.cs +++ b/pkNX.Containers/Misc/FnvHash.cs @@ -15,10 +15,10 @@ public static class FnvHash /// Gets the hash code of the input sequence via the default Fnv1 method. /// /// Input sequence + /// Initial hash value /// Computed hash code - public static ulong HashFnv1_64(IEnumerable input) + public static ulong HashFnv1_64(IEnumerable input, ulong hash = kOffsetBasis_64) { - ulong hash = kOffsetBasis_64; foreach (var c in input) { hash *= kFnvPrime_64; @@ -31,10 +31,10 @@ public static ulong HashFnv1_64(IEnumerable input) /// Gets the hash code of the input sequence via the default Fnv1 method. /// /// Input sequence + /// Initial hash value /// Computed hash code - public static ulong HashFnv1_64(IEnumerable input) + public static ulong HashFnv1_64(IEnumerable input, ulong hash = kOffsetBasis_64) { - ulong hash = kOffsetBasis_64; foreach (var c in input) { hash *= kFnvPrime_64; @@ -47,10 +47,10 @@ public static ulong HashFnv1_64(IEnumerable input) /// Gets the hash code of the input sequence via the alternative Fnv1 method. /// /// Input sequence + /// Initial hash value /// Computed hash code - public static ulong HashFnv1a_64(IEnumerable input) + public static ulong HashFnv1a_64(IEnumerable input, ulong hash = kOffsetBasis_64) { - ulong hash = kOffsetBasis_64; foreach (var c in input) { hash ^= c; @@ -63,10 +63,10 @@ public static ulong HashFnv1a_64(IEnumerable input) /// Gets the hash code of the input sequence via the alternative Fnv1 method. /// /// Input sequence + /// Initial hash value /// Computed hash code - public static ulong HashFnv1a_64(IEnumerable input) + public static ulong HashFnv1a_64(IEnumerable input, ulong hash = kOffsetBasis_64) { - ulong hash = kOffsetBasis_64; foreach (var c in input) { hash ^= c; @@ -83,10 +83,10 @@ public static ulong HashFnv1a_64(IEnumerable input) /// Gets the hash code of the input sequence via the default Fnv1 method. /// /// Input sequence + /// Initial hash value /// Computed hash code - public static uint HashFnv1_32(IEnumerable input) + public static uint HashFnv1_32(IEnumerable input, uint hash = kOffsetBasis_32) { - uint hash = kOffsetBasis_32; foreach (var c in input) { hash *= kFnvPrime_32; @@ -99,10 +99,10 @@ public static uint HashFnv1_32(IEnumerable input) /// Gets the hash code of the input sequence via the alternative Fnv1 method. /// /// Input sequence + /// Initial hash value /// Computed hash code - public static uint HashFnv1a_32(IEnumerable input) + public static uint HashFnv1a_32(IEnumerable input, uint hash = kOffsetBasis_32) { - uint hash = kOffsetBasis_32; foreach (var c in input) { hash ^= c; diff --git a/pkNX.Containers/Misc/GFPack.cs b/pkNX.Containers/Misc/GFPack.cs index bbbeceae..c7adc714 100644 --- a/pkNX.Containers/Misc/GFPack.cs +++ b/pkNX.Containers/Misc/GFPack.cs @@ -127,6 +127,10 @@ public byte[] GetDataFileName(string name) return DecompressedFiles[index]; } + public byte[] GetDataFull(ulong hash) => DecompressedFiles[GetIndexFull(hash)]; + + public byte[] GetDataFullPath(string path) => GetDataFull(FnvHash.HashFnv1a_64(path)); + public void SetDataFileName(string name, byte[] data) { int index = GetIndexFileName(name); @@ -169,7 +173,7 @@ public void LoadFiles(string[] directories, string parent, CompressionType type for (var i = 0; i < files.Length; i++) { var file = files[i]; - var namelong = file.Substring(file.IndexOf(parent, StringComparison.Ordinal)); + var namelong = file[file.IndexOf(parent, StringComparison.Ordinal)..]; ulong hashFull = FnvHash.HashFnv1a_64(namelong); HashAbsolute[i] = new FileHashAbsolute { HashFnv1aPathFull = hashFull }; @@ -211,7 +215,13 @@ private static byte[] Decompress(byte[] encryptedData, int decryptedLength, Comp { CompressionType.None => encryptedData, CompressionType.Zlib => throw new NotSupportedException(nameof(CompressionType.Zlib)), // not implemented - _ => LZ4.Decode(encryptedData, decryptedLength) + CompressionType.Lz4 => LZ4.Decode(encryptedData, decryptedLength), + CompressionType.OodleKraken => Oodle.Decompress(encryptedData, decryptedLength)!, + CompressionType.OodleLeviathan => Oodle.Decompress(encryptedData, decryptedLength)!, + CompressionType.OodleMermaid => Oodle.Decompress(encryptedData, decryptedLength)!, + CompressionType.OodleSelkie => Oodle.Decompress(encryptedData, decryptedLength)!, + CompressionType.OodleHydra => Oodle.Decompress(encryptedData, decryptedLength)!, + _ => throw new ArgumentOutOfRangeException(nameof(type)), }; } @@ -221,7 +231,13 @@ private static byte[] Compress(byte[] decryptedData, CompressionType type) { CompressionType.None => decryptedData, CompressionType.Zlib => throw new NotSupportedException(nameof(CompressionType.Zlib)), // not implemented - _ => LZ4.Encode(decryptedData) + CompressionType.Lz4 => LZ4.Encode(decryptedData), + CompressionType.OodleKraken => Oodle.Compress(decryptedData, out _, OodleFormat.Kraken).ToArray(), + CompressionType.OodleLeviathan => Oodle.Compress(decryptedData, out _, OodleFormat.Leviathan).ToArray(), + CompressionType.OodleMermaid => Oodle.Compress(decryptedData, out _, OodleFormat.Mermaid).ToArray(), + CompressionType.OodleSelkie => Oodle.Compress(decryptedData, out _, OodleFormat.Selkie).ToArray(), + CompressionType.OodleHydra => Oodle.Compress(decryptedData, out _, OodleFormat.Hydra).ToArray(), + _ => throw new ArgumentOutOfRangeException(nameof(type)), }; } @@ -280,7 +296,7 @@ public void Dump(string path, ContainerHandler handler) var data = DecompressedFiles[f.Index]; var subfolder = HashInFolder.Length == 1 ? fn : Path.Combine(dirName.ToString("X16"), fn); - var loc = Path.Combine(path ?? FilePath, subfolder); + var loc = Path.Combine(path, subfolder); FileMitm.WriteAllBytes(loc, data); } } @@ -416,5 +432,10 @@ public enum CompressionType : ushort None = 0, Zlib = 1, Lz4 = 2, + OodleKraken = 3, + OodleLeviathan = 4, + OodleMermaid = 5, + OodleSelkie = 6, + OodleHydra = 7, } } diff --git a/pkNX.Containers/Misc/Oodle.cs b/pkNX.Containers/Misc/Oodle.cs new file mode 100644 index 00000000..15e87ce6 --- /dev/null +++ b/pkNX.Containers/Misc/Oodle.cs @@ -0,0 +1,156 @@ +using System; +using System.Runtime.InteropServices; + +namespace pkNX.Containers +{ + /// + /// Oodle Compression and Decompression wrapper around the external dll. + /// + /// + /// These methods are safely tuned for Span in order to minimize allocation. + /// + public static class Oodle + { + /// + /// Oodle Library Path + /// + private const string OodleLibraryPath = "oo2core_8_win64"; + + /// + /// Oodle64 Decompression Method + /// + [DllImport(OodleLibraryPath, CallingConvention = CallingConvention.Cdecl)] + private static extern long OodleLZ_Decompress(ref byte buffer, long bufferSize, ref byte result, long outputBufferSize, + OodleFuzzSafe fuzz = OodleFuzzSafe.Yes, + OodleCheckCrc crc = OodleCheckCrc.No, + OodleVerbosity verbosity = OodleVerbosity.None, + long context = 0, long e = 0, long callback = 0, long callback_ctx = 0, long scratch = 0, long scratch_size = 0, + OodleThreadPhase threadPhase = OodleThreadPhase.Unthreaded); + + /// + /// Oodle64 Compression Method + /// + [DllImport(OodleLibraryPath)] + private static extern long OodleLZ_Compress(OodleFormat format, ref byte buffer, long bufferSize, ref byte result, OodleCompressionLevel level, + long opts = 0, long context = 0, long unused = 0, long scratch = 0, long scratch_size = 0); + + /// + /// Decompresses a span of Oodle Compressed bytes (Requires Oodle DLL) + /// + /// Input Compressed Data + /// Decompressed Size + /// Resulting Array if success, otherwise null. + public static byte[]? Decompress(ReadOnlySpan input, long decompressedLength) + { + var result = new byte[decompressedLength]; + return Decompress(input, result); + } + + private static byte[]? Decompress(ReadOnlySpan input, byte[] result) + { + var dest = result.AsSpan(); + long decodedSize = OodleLZ_Decompress(ref MemoryMarshal.GetReference(input), input.Length, ref MemoryMarshal.GetReference(dest), result.Length); + if (decodedSize == 0) + return null; // failed + return result; + } + + /// + /// Compresses a span of bytes to Oodle Compressed bytes (Requires Oodle DLL) + /// + /// Input Decompressed Data + /// Actual Compressed Data size + /// Compression format to use + /// Compression setting to use + /// Span of compressed data with remainder aligned working bytes. + public static Span Compress(ReadOnlySpan input, out int compressedSize, + OodleFormat format = OodleFormat.Kraken, OodleCompressionLevel level = OodleCompressionLevel.Optimal2) + { + var maxSize = GetCompressedBufferSizeNeeded(input.Length); + var result = new byte[maxSize].AsSpan(); + return Compress(input, result, out compressedSize, format, level); + } + + private static Span Compress(ReadOnlySpan input, Span result, out int compressedSize, OodleFormat format, OodleCompressionLevel level) + { + var encodedSize = OodleLZ_Compress(format, ref MemoryMarshal.GetReference(input), input.Length, ref MemoryMarshal.GetReference(result), level); + + // Oodle's compressed result leaves data after the "compressed length" return index. + // Return an aligned span (ensuring length is a multiple of 4). + // Retaining these unused bytes matches the behavior observed in New Pokémon Snap DRPF files. + compressedSize = (int)encodedSize; + var align = (compressedSize + 3) & ~3; + return result[..align]; + } + + /// + /// Gets the dimension required to compress the data. + /// + /// + /// + private static long GetCompressedBufferSizeNeeded(long inputSize) + { + return inputSize + (274 * ((inputSize + 0x3FFFF) / 0x40000)); + } + } + + public enum OodleFormat : uint + { + LZH = 0, + LZHLW = 1, + LZNIB = 2, + None = 3, + LZB16 = 4, + LZBLW = 5, + LZA = 6, + LZNA = 7, + Kraken = 8, + Mermaid = 9, + BitKnit = 10, + Selkie = 11, + Hydra = 12, + Leviathan = 13, + } + + public enum OodleCompressionLevel : ulong + { + None = 0, + SuperFast = 1, + VeryFast = 2, + Fast = 3, + Normal = 4, + Optimal1 = 5, + Optimal2 = 6, + Optimal3 = 7, + Optimal4 = 8, + Optimal5 = 9, + } + + public enum OodleFuzzSafe + { + No = 0, + Yes = 1, + } + + public enum OodleCheckCrc + { + No = 0, + Yes = 1, + } + + public enum OodleVerbosity + { + None = 0, + Max = 3, + } + + [Flags] + public enum OodleThreadPhase + { + Invalid = 0, + ThreadPhase1 = 1, + ThreadPhase2 = 2, + + Unthreaded = ThreadPhase1 | ThreadPhase2, // 3 + } +} diff --git a/pkNX.Containers/pkNX.Containers.csproj b/pkNX.Containers/pkNX.Containers.csproj index 04851892..ff322c46 100644 --- a/pkNX.Containers/pkNX.Containers.csproj +++ b/pkNX.Containers/pkNX.Containers.csproj @@ -3,12 +3,14 @@ netstandard2.0;net461 Packing & Unpacking - 9 + 10 enable + + diff --git a/pkNX.Game/Editors/TypeChartEditor.cs b/pkNX.Game/Editors/TypeChartEditor.cs index 1085d3c7..19b7b7d9 100644 --- a/pkNX.Game/Editors/TypeChartEditor.cs +++ b/pkNX.Game/Editors/TypeChartEditor.cs @@ -24,14 +24,13 @@ public void Randomize() private static byte GetEffectiveness(int rv) { - if (rv < 2) // 2% - return (byte)TypeEffectiveness.Immune; - if (rv < 19) // 17% - return (byte)TypeEffectiveness.NotVery; - if (rv < 36) // 17% - return (byte)TypeEffectiveness.Super; - - return (byte)TypeEffectiveness.Normal; + return rv switch + { + < 2 => (byte)TypeEffectiveness.Immune, // 2% + < 19 => (byte)TypeEffectiveness.NotVery, // 17% + < 36 => (byte)TypeEffectiveness.Super, // 17% + _ => (byte)TypeEffectiveness.Normal, + }; } private static readonly uint[] Colors = @@ -42,7 +41,7 @@ private static byte GetEffectiveness(int rv) 0, // unused 0xFFFFFFFF, 0, 0, 0, // unused - 0xFF008000 + 0xFF008000, }; public static byte[] GetTypeChartImageData(int itemsize, int itemsPerRow, byte[] vals, out int width, out int height) diff --git a/pkNX.Game/File/GameFile.cs b/pkNX.Game/File/GameFile.cs index 42ce5b6b..548fcb13 100644 --- a/pkNX.Game/File/GameFile.cs +++ b/pkNX.Game/File/GameFile.cs @@ -175,5 +175,20 @@ public enum GameFile /// Symbol Behavior Definition SymbolBehave, + + /// Area Resident Archive + Resident, + + /// "PokeEncount" Rate Multipler Archive + EncounterRateTable, + + /// huge_outbreak.bin + Outbreak, + + /// wazashop_table.bin + MoveShop, + + /// "PokeMisc" Details about a given Species-Form not stored in + PokeMisc, } } diff --git a/pkNX.Game/File/GameFileMapping.cs b/pkNX.Game/File/GameFileMapping.cs index 37491469..911beaeb 100644 --- a/pkNX.Game/File/GameFileMapping.cs +++ b/pkNX.Game/File/GameFileMapping.cs @@ -23,7 +23,7 @@ public class GameFileMapping internal IFileContainer GetFile(GameFile file, int language) { - if (file == GameFile.GameText || file == GameFile.StoryText) + if (file is GameFile.GameText or GameFile.StoryText) file += language + 1; // shift to localized language if (Cache.TryGetValue(file, out var container)) @@ -67,6 +67,7 @@ public static IReadOnlyCollection GetMapping(GameVersion game GameVersion.SW => SWSH, GameVersion.SH => SWSH, GameVersion.SWSH => SWSH, + GameVersion.PLA => PLA, GameVersion.ORASDEMO => AO, GameVersion.ORAS => AO, GameVersion.SMDEMO => SMDEMO, @@ -412,6 +413,58 @@ public static IReadOnlyCollection GetMapping(GameVersion game // Models bin\archive\pokemon // pretty much everything is obviously named :) }; + + /// + /// Sword + /// + private static readonly GameFileReference[] PLA = + { + new(GameFile.TrainerData, "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"), + // 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(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"), + // 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(GameFile.ItemStats, ContainerType.SingleFile, "bin", "pml", "item", "item.dat"), + new(GameFile.Evolutions, "bin", "pml", "evolution"), + new(GameFile.PersonalStats, ContainerType.SingleFile, "bin", "pml", "personal", "personal_data_total.perbin"), + new(GameFile.MoveStats, "bin", "pml", "waza"), + new(GameFile.EncounterStatic, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_event_encount.bin"), + new(GameFile.EncounterTrade, ContainerType.SingleFile, "bin", "script_event_data", "field_trade.bin"), + new(GameFile.EncounterGift, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_add.bin"), + new(GameFile.Learnsets, ContainerType.SingleFile, "bin", "pml", "waza_oboe", "waza_oboe_total.wazaoboe"), + + new(GameFile.Resident, ContainerType.SingleFile, "bin", "archive", "field", "resident_release.gfpak"), + + new(GameFile.EncounterRateTable, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_encount.bin"), + new(GameFile.PokeMisc, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_misc.bin"), + new(GameFile.Outbreak, ContainerType.SingleFile, "bin", "field", "encount", "huge_outbreak.bin"), + new(GameFile.MoveShop, ContainerType.SingleFile, "bin", "appli", "wazaremember", "bin", "wazashop_table.bin"), + + // Cutscenes bin\demo + // Models bin\archive\pokemon + // pretty much everything is obviously named :) + }; #endregion #region Split Versions diff --git a/pkNX.Game/GameLocation.cs b/pkNX.Game/GameLocation.cs index a783cb12..c9b5301b 100644 --- a/pkNX.Game/GameLocation.cs +++ b/pkNX.Game/GameLocation.cs @@ -67,6 +67,8 @@ public static GameLocation GetGame(string dir) private const int FILECOUNT_SWSH_1 = 41951; // Ver. 1.1.0 update (Galarian Slowpoke) private const int FILECOUNT_SWSH_2 = 46867; // Ver. 1.2.0 update (Isle of Armor) private const int FILECOUNT_SWSH_3 = 50494; // Ver. 1.3.0 update (Crown Tundra) + private const int FILECOUNT_LA = 18_370; + private const int FILECOUNT_LA_1 = 18_371; // Ver. 1.0.1 private static GameVersion GetGameFromCount(int fileCount, string romfs, string exefs) { @@ -112,6 +114,9 @@ private static GameVersion GetGameFromCount(int fileCount, string romfs, string return GetTitleID() == "0100ABF008968000" ? GameVersion.SW : GameVersion.SH; } + case FILECOUNT_LA or FILECOUNT_LA_1: + return GameVersion.PLA; + default: return GameVersion.Invalid; } diff --git a/pkNX.Game/GameManager.cs b/pkNX.Game/GameManager.cs index 4c52f097..0ae48157 100644 --- a/pkNX.Game/GameManager.cs +++ b/pkNX.Game/GameManager.cs @@ -117,6 +117,7 @@ public static GameManager GetManager(GameLocation loc, int language) { 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)) }; } diff --git a/pkNX.Game/GameManagerGG.cs b/pkNX.Game/GameManagerGG.cs index ad09b758..c238bd53 100644 --- a/pkNX.Game/GameManagerGG.cs +++ b/pkNX.Game/GameManagerGG.cs @@ -55,7 +55,7 @@ public override void Initialize() }, EvolutionData = new DataCache(GetFilteredFolder(GameFile.Evolutions)) { - Create = (data) => new EvolutionSet7(data), + Create = data => new EvolutionSet7(data), Write = evo => evo.Write(), }, }; diff --git a/pkNX.Game/GameManagerPLA.cs b/pkNX.Game/GameManagerPLA.cs new file mode 100644 index 00000000..ae39d18d --- /dev/null +++ b/pkNX.Game/GameManagerPLA.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; +using pkNX.Containers; +using pkNX.Structures.FlatBuffers; + +namespace pkNX.Game +{ + public class GameManagerPLA : GameManager + { + public GameManagerPLA(GameLocation rom, int language) : base(rom, language) { } + private string PathNPDM => Path.Combine(PathExeFS, "main.npdm"); + private string TitleID => BitConverter.ToUInt64(File.ReadAllBytes(PathNPDM), 0x290).ToString("X16"); + + protected override void SetMitm() + { + var basePath = Path.GetDirectoryName(ROM.RomFS); + var tid = ROM.ExeFS != null ? TitleID : "arceus"; + var redirect = Path.Combine(basePath, tid); + FileMitm.SetRedirect(basePath, redirect); + } + + private Learnset8a Learn; + + public override void Initialize() + { + base.Initialize(); + + // initialize gametext + ResetText(); + + // initialize common structures + var personal = GetFile(GameFile.PersonalStats)[0]; + var learn = this[GameFile.Learnsets][0]; + Learn = FlatBufferConverter.DeserializeFrom(learn); + + var move = this[GameFile.MoveStats]; + ((FolderContainer)move).Initialize(); + //Data = new GameData + //{ + // MoveData = new DataCache(move) + // { + // Create = FlatBufferConverter.DeserializeFrom, + // Write = z => FlatBufferConverter.SerializeFrom((Waza8a)z), + // }, + // LevelUpData = new DataCache(Array.Empty()) + // { + // Create = z => new Learnset8(z), + // Write = z => z.Write(), + // }, + // + // // folders + // PersonalData = new PersonalTable(personal, Game), + // EvolutionData = new DataCache(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 => z.Write()).ToArray(); + var learn = this[GameFile.Learnsets]; + //learn[0] = Learn.Entries.SelectMany(z => z.Write()).ToArray(); + } + } +} \ No newline at end of file diff --git a/pkNX.Game/Misc/PLAInfo.cs b/pkNX.Game/Misc/PLAInfo.cs new file mode 100644 index 00000000..5baa46ad --- /dev/null +++ b/pkNX.Game/Misc/PLAInfo.cs @@ -0,0 +1,89 @@ +namespace pkNX.Game +{ + public static class PLAInfo + { + + public static readonly string[][] AreaNames = + { + new[] { + "ha_area00", + "ha_area00_s01", + "ha_area00_s02", + "ha_area00_s03_a", + "ha_area00_s03_b", + "ha_area00_s03_c", + "ha_area00_s03_d", + "ha_area00_s03_e", + "ha_area00_s03_f", + "ha_area00_s03_g", + "ha_area00_s03_h", + "ha_area00_s03_i", + "ha_area00_s03_j", + "ha_area00_s03_k", + "ha_area00_s03_l", + "ha_area00_s03_m", + "ha_area00_s03_n", + "ha_area00_s03_o", + "ha_area00_s03_p", + "ha_area00_s03_q", + "ha_area00_s03_r", + "ha_area00_s03_s", + "ha_area00_s03_t", + "ha_area00_s06", + "ha_area00_s07", + "ha_area00_s09", + "ha_area00_s12_a", + "ha_area00_s12_b", + "ha_area00_s12_c", + "ha_area00_s12_d", + "ha_area00_s16_a", + "ha_area00_s16_b", + }, + new[] { + "ha_area01", + "ha_area01_s01", + }, + new[] { + "ha_area02", + "ha_area02_s01", + "ha_area02_s01_a", + "ha_area02_s01_b", + "ha_area02_s01_c", + "ha_area02_s01_d", + "ha_area02_s02", + }, + new[] { + "ha_area03", + "ha_area03_s01", + "ha_area03_s02", + "ha_area03_s03", + "ha_area03_s04", + }, + new[] { + "ha_area04", + "ha_area04_s01", + "ha_area04_s02", + "ha_area04_s03", + "ha_area04_s04", + }, + new[] { + "ha_area05", + "ha_area05_s01", + "ha_area05_s02", + "ha_area05_s03", + "ha_area05_s04", + "ha_area05_s04_a", + "ha_area05_s04_b", + "ha_area05_s04_c", + "ha_area05_s04_d", + }, + new[] { + "ha_area06", + "ha_area06_s01", + }, + new[] { + "ha_area11", + } + }; + } +} diff --git a/pkNX.Game/Misc/SWSHEncounterType.cs b/pkNX.Game/Misc/SWSHEncounterType.cs new file mode 100644 index 00000000..c8a99e39 --- /dev/null +++ b/pkNX.Game/Misc/SWSHEncounterType.cs @@ -0,0 +1,16 @@ +namespace pkNX.Game; + +public enum SWSHEncounterType +{ + Normal_Weather, + Overcast, + Raining, + Thunderstorm, + Intense_Sun, + Snowing, + Snowstorm, + Sandstorm, + Heavy_Fog, + Shaking_Trees, + Fishing, +} diff --git a/pkNX.Game/Misc/SWSHInfo.cs b/pkNX.Game/Misc/SWSHInfo.cs index 22a2b108..1897e9d3 100644 --- a/pkNX.Game/Misc/SWSHInfo.cs +++ b/pkNX.Game/Misc/SWSHInfo.cs @@ -628,40 +628,4 @@ public static class SWSHInfo {0x9BDD6D11FFBEDA3F, (byte)Surfing}, // at Ballimere Lake (Surfing) }; } - - public enum SWSHEncounterType - { - Normal_Weather, - Overcast, - Raining, - Thunderstorm, - Intense_Sun, - Snowing, - Snowstorm, - Sandstorm, - Heavy_Fog, - Shaking_Trees, - Fishing, - }; - - public enum SWSHSlotType: ushort - { - SymbolMain, - SymbolMain2, - SymbolMain3, - - HiddenMain, // Table with the tree/fishing slots - HiddenMain2, - - Surfing, - Surfing2, - Sky, - Sky2, - Ground, - Ground2, - Sharpedo, - - OnlyFishing, - Inaccessible, - }; } diff --git a/pkNX.Game/Misc/SWSHSlotType.cs b/pkNX.Game/Misc/SWSHSlotType.cs new file mode 100644 index 00000000..76b34d77 --- /dev/null +++ b/pkNX.Game/Misc/SWSHSlotType.cs @@ -0,0 +1,22 @@ +namespace pkNX.Game; + +public enum SWSHSlotType : ushort +{ + SymbolMain, + SymbolMain2, + SymbolMain3, + + HiddenMain, // Table with the tree/fishing slots + HiddenMain2, + + Surfing, + Surfing2, + Sky, + Sky2, + Ground, + Ground2, + Sharpedo, + + OnlyFishing, + Inaccessible, +} diff --git a/pkNX.Game/Text/TextMapping.cs b/pkNX.Game/Text/TextMapping.cs index 23f2b1e7..8bf2ecc8 100644 --- a/pkNX.Game/Text/TextMapping.cs +++ b/pkNX.Game/Text/TextMapping.cs @@ -23,6 +23,7 @@ public static IReadOnlyCollection GetMapping(GameVersion game) GameVersion.SW => SWSH, GameVersion.SH => SWSH, GameVersion.SWSH => SWSH, + GameVersion.PLA => PLA, _ => null }; } @@ -173,5 +174,32 @@ public static IReadOnlyCollection GetMapping(GameVersion game) new("ribbon.dat", TextName.RibbonMark), new("poke_memory_feeling.dat", TextName.MemoryFeelings), }; + + + private static readonly TextReference[] PLA = + { + new("iteminfo.dat", TextName.ItemFlavor), + new("itemname.dat", TextName.ItemNames), + new("monsname.dat", TextName.SpeciesNames), + new("place_name_indirect.dat", TextName.metlist_00000), + new("place_name_spe.dat", TextName.metlist_30000), + new("place_name_out.dat", TextName.metlist_40000), + new("place_name_per.dat", TextName.metlist_60000), + new("seikaku.dat", TextName.Natures), + new("tokusei.dat", TextName.AbilityNames), + new("tokuseiinfo.dat", TextName.AbilityFlavor), + new("trname.dat", TextName.TrainerNames), + new("trtype.dat", TextName.TrainerClasses), + new("trmsg.dat", TextName.TrainerText), + new("typename.dat", TextName.Types), + new("wazainfo.dat", TextName.MoveFlavor), + new("wazaname.dat", TextName.MoveNames), + new("zkn_form.dat", TextName.Forms), + new("zkn_type.dat", TextName.SpeciesClassifications), + new("zukan_comment_A.dat", TextName.PokedexEntry1), + new("zukan_comment_B.dat", TextName.PokedexEntry2), + new("ribbon.dat", TextName.RibbonMark), + new("poke_memory_feeling.dat", TextName.MemoryFeelings), + }; } } \ No newline at end of file diff --git a/pkNX.Game/pkNX.Game.csproj b/pkNX.Game/pkNX.Game.csproj index f5a57397..cfd0c190 100644 --- a/pkNX.Game/pkNX.Game.csproj +++ b/pkNX.Game/pkNX.Game.csproj @@ -3,9 +3,14 @@ netstandard2.0;net461 Game Data Manager - 9 + 10 + + + + + diff --git a/pkNX.Randomization/Randomizers/EvolutionRandomizer.cs b/pkNX.Randomization/Randomizers/EvolutionRandomizer.cs index 670740fc..e11e8a65 100644 --- a/pkNX.Randomization/Randomizers/EvolutionRandomizer.cs +++ b/pkNX.Randomization/Randomizers/EvolutionRandomizer.cs @@ -113,10 +113,10 @@ private void ReplaceTradeMethod(EvolutionMethod evo, int species, int evoIndex) } } - private void MakeEvolveEveryLevel(EvolutionSet evos, int species) + private static void MakeEvolveEveryLevel(EvolutionSet evos, int species) { var evoSet = evos.PossibleEvolutions; - var evoNew = new EvolutionMethod() + evoSet[0] = new EvolutionMethod { Argument = 0, // clear Form = 0, // randomized later @@ -125,8 +125,6 @@ private void MakeEvolveEveryLevel(EvolutionSet evos, int species) Species = species, // randomized later }; - evoSet[0] = evoNew; - if (evoSet[1].HasData) // has other branched evolutions; remove them { for (int i = 1; i < evoSet.Length; i++) diff --git a/pkNX.Randomization/Randomizers/FormRandomizer.cs b/pkNX.Randomization/Randomizers/FormRandomizer.cs index 581ccad2..b5b39c25 100644 --- a/pkNX.Randomization/Randomizers/FormRandomizer.cs +++ b/pkNX.Randomization/Randomizers/FormRandomizer.cs @@ -63,7 +63,7 @@ public int GetRandomForme(int species, bool mega, bool fused, bool alola, bool g } } - if (Personal.TableLength == 980 && (species == (int)Pikachu || species == (int)Eevee)) // gg tableB -- no starters, they crash trainer battles. + if (Personal.TableLength == 980 && species is (int)Pikachu or (int)Eevee) // gg tableB -- no starters, they crash trainer battles. return 0; if (alola && Legal.EvolveToAlolanForms.Contains(species)) return Util.Random.Next(2); @@ -74,14 +74,11 @@ public int GetRandomForme(int species, bool mega, bool fused, bool alola, bool g return 0; } - public int GetInvalidForm(int species, bool galar, PersonalTable stats) + public static int GetInvalidForm(int species, bool galar, PersonalTable stats) => species switch { - return species switch - { - (int)Pikachu when stats.TableLength == 1192 => 8, // LGPE Partner Pikachu - (int)Slowbro when galar => 1, // Mega Slowbro - _ => throw new ArgumentOutOfRangeException(nameof(species)) - }; - } + (int)Pikachu when stats.TableLength == 1192 => 8, // LGPE Partner Pikachu + (int)Slowbro when galar => 1, // Mega Slowbro + _ => throw new ArgumentOutOfRangeException(nameof(species)) + }; } } diff --git a/pkNX.Randomization/Randomizers/TrainerRandomizer.cs b/pkNX.Randomization/Randomizers/TrainerRandomizer.cs index 9b55c47e..a9f24fae 100644 --- a/pkNX.Randomization/Randomizers/TrainerRandomizer.cs +++ b/pkNX.Randomization/Randomizers/TrainerRandomizer.cs @@ -255,7 +255,7 @@ private void UpdatePKMFromSettings(TrainerPoke pk) return; c.Species = AllowedGigantamaxes[Util.Random.Next(AllowedGigantamaxes.Length)]; - c.Form = c.Species == (int)Species.Pikachu || c.Species == (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.GetRandomForme(c.Species, false, false, false, false, Personal.Table); // Pikachu & Meowth altforms can't gmax } if (Settings.MaxDynamaxLevel && c.CanDynamax) c.DynamaxLevel = 10; diff --git a/pkNX.Randomization/Util.cs b/pkNX.Randomization/Util.cs index 3c53ec61..a15f1ca2 100644 --- a/pkNX.Randomization/Util.cs +++ b/pkNX.Randomization/Util.cs @@ -22,9 +22,7 @@ public static void Shuffle(IList array) for (int i = 0; i < n; i++) { int r = i + (int)(Random.NextDouble() * (n - i)); - var t = array[r]; - array[r] = array[i]; - array[i] = t; + (array[r], array[i]) = (array[i], array[r]); } } @@ -50,8 +48,8 @@ public static uint GetHexValue(string s) return string.IsNullOrWhiteSpace(str) ? 0 : Convert.ToUInt32(str, 16); } - private static bool IsHex(char c) => (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); - private static string TitleCase(string word) => char.ToUpper(word[0]) + word.Substring(1, word.Length - 1).ToLower(); + private static bool IsHex(char c) => c is >= '0' and <= '9' or >= 'A' and <= 'F' or >= 'a' and <= 'f'; + private static string TitleCase(string word) => char.ToUpper(word[0]) + word[1..].ToLower(); /// /// Filters the string down to only valid hex characters, returning a new string. diff --git a/pkNX.Randomization/pkNX.Randomization.csproj b/pkNX.Randomization/pkNX.Randomization.csproj index d28f5399..45972359 100644 --- a/pkNX.Randomization/pkNX.Randomization.csproj +++ b/pkNX.Randomization/pkNX.Randomization.csproj @@ -3,10 +3,15 @@ netstandard2.0;net461 Randomizer Utility - 9 + 10 enable + + + + + diff --git a/pkNX.Sprites/FormConverter.cs b/pkNX.Sprites/FormConverter.cs index d2e62e39..f59c5312 100644 --- a/pkNX.Sprites/FormConverter.cs +++ b/pkNX.Sprites/FormConverter.cs @@ -15,7 +15,7 @@ public static bool IsTotemForm(int species, int form, int generation = 7) if (!Legal.Totem_USUM.Contains(species)) return false; if (species == (int)Mimikyu) - return form == 2 || form == 3; + return form is 2 or 3; if (Legal.Totem_Alolan.Contains(species)) return form == 2; return form == 1; diff --git a/pkNX.Sprites/SpriteBuilder.cs b/pkNX.Sprites/SpriteBuilder.cs index 4ed9b957..b0bfe017 100644 --- a/pkNX.Sprites/SpriteBuilder.cs +++ b/pkNX.Sprites/SpriteBuilder.cs @@ -101,9 +101,9 @@ private Image GetBaseImageFallback(int species, int form, int gender, bool shiny private Image LayerOverImageItem(Image baseImage, int item, int generation) { Image itemimg = (Image?)Resources.ResourceManager.GetObject(GetItemResourceName(item)) ?? Resources.bitem_unk; - if (328 <= item && item <= 419) // gen2/3/4 TM + if (item is >= 328 and <= 419) // gen2/3/4 TM itemimg = ItemTM; - else if (1130 <= item && item <= 1229) // Gen8 TR + 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 diff --git a/pkNX.Sprites/SpriteName.cs b/pkNX.Sprites/SpriteName.cs index 4476c011..d3f85d75 100644 --- a/pkNX.Sprites/SpriteName.cs +++ b/pkNX.Sprites/SpriteName.cs @@ -9,11 +9,11 @@ public static class SpriteName public static bool AllowShinySprite { get; set; } = true; public static bool AllowGigantamaxSprite { get; set; } = true; - private const string Separator = "_"; - private const string Cosplay = "c"; - private const string Shiny = "s"; + private const char Separator = '_'; + private const char Cosplay = 'c'; + private const char Shiny = 's'; private const string Gigantamax = "gmax"; - private const string GGStarter = "p"; + private const char GGStarter = 'p'; public static string GetResourceStringBall(int ball) => $"_ball{ball}"; @@ -25,15 +25,15 @@ public static string GetResourceStringSprite(int species, int form, int gender, if (SpeciesDefaultFormSprite.Contains(species) && !gmax) // Species who show their default sprite regardless of Form form = 0; - if (gmax && (species == (int)Species.Toxtricity || species == (int)Species.Alcremie)) // same sprites for all altform gmaxes + 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 >= (int)Species.Scatterbug && species <= (int)Species.Vivillon: // save file specific + case 30 when species is >= (int)Species.Scatterbug and <= (int)Species.Vivillon: // save file specific form = 0; break; - case 31 when species == (int)Species.Unown || species == (int)Species.Deerling || species == (int)Species.Sawsbuck: // Random + case 31 when species is (int)Species.Unown or (int)Species.Deerling or (int)Species.Sawsbuck: // Random form = 0; break; } @@ -43,7 +43,8 @@ public static string GetResourceStringSprite(int species, int form, int gender, if (form != 0) { - sb.Append(Separator); sb.Append(form); + sb.Append(Separator) + .Append(form); if (species == (int)Species.Pikachu) { diff --git a/pkNX.Sprites/pkNX.Sprites.csproj b/pkNX.Sprites/pkNX.Sprites.csproj index 37cec757..c859b79c 100644 --- a/pkNX.Sprites/pkNX.Sprites.csproj +++ b/pkNX.Sprites/pkNX.Sprites.csproj @@ -2,7 +2,7 @@ net5.0;net461 - 9 + 10 enable true @@ -12,6 +12,8 @@ + + True diff --git a/pkNX.Structures.FlatBuffers/Arceus/ArchiveContents8a.cs b/pkNX.Structures.FlatBuffers/Arceus/ArchiveContents8a.cs new file mode 100644 index 00000000..50719e2f --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/ArchiveContents8a.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ArchiveContents8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public ArchiveContent8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ArchiveContent8a +{ + [FlatBufferItem(00)] public ulong Field_00 { get; set; } + [FlatBufferItem(01)] public ulong[] Field_01 { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Condition/Condition8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Condition/Condition8a.cs new file mode 100644 index 00000000..09d1a9d3 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Condition/Condition8a.cs @@ -0,0 +1,16 @@ +using FlatSharp.Attributes; + +// ReSharper disable UnusedMember.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferEnum(typeof(ulong))] +public enum Condition8a : ulong +{ + GreaterThanEqual = 0x34F605C97C571896, + LessThanEqual = 0x488B1F9FBC949365, + NotEqual = 0x88C99E99F1FC6C55, + Equal = 0xB763CBE88B6F844F, + None = 0xCBF29CE484222645, + Between = 0xE5E34D77DC75E2EF, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Condition/Condition8aUtil.cs b/pkNX.Structures.FlatBuffers/Arceus/Condition/Condition8aUtil.cs new file mode 100644 index 00000000..5eec1514 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Condition/Condition8aUtil.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; + +namespace pkNX.Structures.FlatBuffers; + +public static class Condition8aUtil +{ + public static string GetConditionTypeSummary(this IHasCondition8a cond) + { + if (!Enum.IsDefined(typeof(ConditionType8a), cond.ConditionTypeID)) + return $"0x{(ulong)cond.ConditionTypeID:X16}"; + //throw new ArgumentOutOfRangeException(nameof(cond.ConditionTypeID), cond.ConditionTypeID, "Unknown."); + return $"{cond.ConditionTypeID}"; + } + + public static string GetConditionArgsSummary(this IHasCondition8a cond) + { + var args = new[] { cond.ConditionArg1, cond.ConditionArg2, cond.ConditionArg3, cond.ConditionArg4, cond.ConditionArg5 }; + var firstEmpty = -1; + for (var i = 0; i < args.Length; i++) + { + if (firstEmpty >= 0 && !string.IsNullOrEmpty(args[i])) + throw new ArgumentException($"Invalid ConditionArg at index {i}!"); + else if (firstEmpty < 0 && string.IsNullOrEmpty(args[i])) + firstEmpty = i; + } + + return string.Join(", ", args.Select(s => $"\"{s}\"").Take(firstEmpty >= 0 ? firstEmpty : args.Length)); + } + + public static string GetConditionSummary(this IHasCondition8a cond) + { + if (!Enum.IsDefined(typeof(Condition8a), cond.ConditionID)) + throw new ArgumentOutOfRangeException(nameof(cond.ConditionID), cond.ConditionID, "Unknown."); + return $"{cond.ConditionID}({GetConditionArgsSummary(cond)})"; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Condition/ConditionType8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Condition/ConditionType8a.cs new file mode 100644 index 00000000..a8824704 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Condition/ConditionType8a.cs @@ -0,0 +1,23 @@ +using FlatSharp.Attributes; + +// ReSharper disable UnusedMember.Global +#pragma warning disable RCS1154 // Sort enum members. + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferEnum(typeof(ulong))] +public enum ConditionType8a : ulong +{ + FlagComparison = 0x3DDD5FEB4A68A149, // "flag_comparison + FlagComparisonMultiAnd = 0x1474C7C46A813955, // "flag_comparison_multi_and" + FlagComparisonMultiOr = 0x99A60B9D95356789, // "flag_comparison_multi_or" + Force = 0x344570C1301A7584, // "force" + MkrgCondition = 0xF8ADF49E06E61C30, // "mkrg_condition + PhaseCondition = 0x8E77D62D1E38721E, // "phase_condition" + PokeGetSelf = 0x7AAB4D6FEBAC4C0E, // "poke_get_self" + ProgressWorkCondition = 0x43C4820F12B5385C, // "progress_work_condition" + Probability = 0x5E53C6367A2D36BC, // "probability" + QuestProgressCondition = 0x2041C674206FE0A7, // "quest_progress_condition" + WorkCondition = 0x2557279B949C81D6, // "work_condition + None = 0xCBF29CE484222645, // "" +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Event/Trigger8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Event/Trigger8a.cs new file mode 100644 index 00000000..68d06b93 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Event/Trigger8a.cs @@ -0,0 +1,56 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class TriggerTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public Trigger8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Trigger8a +{ + [FlatBufferItem(00)] public Trigger8a_F00 Field_00 { get; set; } = new(); + [FlatBufferItem(01)] public Trigger8a_F01[] Field_01 { get; set; } = Array.Empty(); + [FlatBufferItem(02)] public Trigger8a_F02[] Field_02 { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Trigger8a_F00 +{ + [FlatBufferItem(00)] public ulong Field_00 { get; set; } + [FlatBufferItem(01)] public ulong Field_01 { get; set; } + [FlatBufferItem(02)] public string Field_02 { get; set; } = string.Empty; + [FlatBufferItem(03)] public string Field_03 { get; set; } = string.Empty; + [FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; // assumed +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Trigger8a_F01 +{ + [FlatBufferItem(00)] public ulong Field_00 { get; set; } + [FlatBufferItem(01)] public byte Field_01 { get; set; } // unk + [FlatBufferItem(02)] public string Field_02 { get; set; } = string.Empty; + [FlatBufferItem(03)] public string Field_03 { get; set; } = string.Empty; // assumed + [FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; // assumed +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Trigger8a_F02 +{ + [FlatBufferItem(00)] public ulong Field_00 { get; set; } + [FlatBufferItem(01)] public string[] Field_01 { get; set; } = Array.Empty(); + [FlatBufferItem(02)] public string[] Field_02 { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/EvolutionTable8.cs b/pkNX.Structures.FlatBuffers/Arceus/EvolutionTable8.cs new file mode 100644 index 00000000..008733a5 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/EvolutionTable8.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel; +using System.IO; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EvolutionTable8 : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public EvolutionSet8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EvolutionSet8a +{ + [FlatBufferItem(0)] public ushort Index { get; set; } + [FlatBufferItem(1)] public ushort Form { get; set; } + [FlatBufferItem(2)] public EvolutionEntry8a[]? Table { get; set; } = Array.Empty(); + + public byte[] Write() + { + if (Table is null) + return Array.Empty(); + + using MemoryStream ms = new(); + using BinaryWriter bw = new(ms); + foreach (var evo in Table) + { + // ReSharper disable RedundantCast + bw.Write((ushort)evo.Method); + bw.Write((ushort)evo.Argument); + bw.Write((ushort)evo.Species); + bw.Write((sbyte)evo.Form); + bw.Write((byte)evo.Level); + // ReSharper restore RedundantCast + } + return ms.ToArray(); + } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EvolutionEntry8a +{ + [FlatBufferItem(0)] public ushort Method { get; set; } + [FlatBufferItem(1)] public ushort Argument { get; set; } + [FlatBufferItem(2)] public ushort Species { get; set; } + [FlatBufferItem(3)] public byte Form { get; set; } + [FlatBufferItem(4)] public byte Level { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/ISlotTableConsumer.cs b/pkNX.Structures.FlatBuffers/Arceus/ISlotTableConsumer.cs new file mode 100644 index 00000000..a337086d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/ISlotTableConsumer.cs @@ -0,0 +1,6 @@ +namespace pkNX.Structures.FlatBuffers; + +public interface ISlotTableConsumer +{ + bool UsesTable(ulong table); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/KeyAssignmentTable.cs b/pkNX.Structures.FlatBuffers/Arceus/KeyAssignmentTable.cs new file mode 100644 index 00000000..b1966d1e --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/KeyAssignmentTable.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class KeyAssignmentTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public KeyAssignment[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class KeyAssignment +{ + [FlatBufferItem(00)] public string Field_00 { get; set; } = string.Empty; + [FlatBufferItem(01)] public string[] Field_01 { get; set; } = Array.Empty(); + [FlatBufferItem(02)] public byte Field_02 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/LandmarkItemSpawnTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/LandmarkItemSpawnTable8a.cs new file mode 100644 index 00000000..b6214aa1 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/LandmarkItemSpawnTable8a.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using FlatSharp.Attributes; +using pkNX.Containers; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LandmarkItemSpawnTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public LandmarkItemSpawn8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LandmarkItemSpawn8a +{ + [FlatBufferItem(00)] public ulong LandmarkItemSpawnTableID { get; set; } + [FlatBufferItem(01)] public int Field_01 { get; set; } + [FlatBufferItem(02)] public int Field_02 { get; set; } + [FlatBufferItem(03)] public LandmarkItemSpawn8a_F03[] Field_03 { get; set; } = Array.Empty(); + [FlatBufferItem(04)] public int Field_04 { get; set; } + [FlatBufferItem(05)] public byte Field_05 { get; set; } // unused + [FlatBufferItem(06)] public byte Field_06 { get; set; } // unused + [FlatBufferItem(07)] public ulong EncounterTableID { get; set; } + [FlatBufferItem(08)] public byte Field_08 { get; set; } // unused + [FlatBufferItem(09)] public int Field_09 { get; set; } + + public bool UsesTable(ulong table) => EncounterTableID == table; + + public string NameSummary + { + get + { + var map = GetNameMap(); + if (map.TryGetValue(LandmarkItemSpawnTableID, out var name)) + return $"\"{name}\""; + + return $"0x{LandmarkItemSpawnTableID:X16}"; + } + } + + private static IReadOnlyDictionary? _spawnerNameMap; + private static IReadOnlyDictionary GetNameMap() => _spawnerNameMap ??= GenerateSpawnerNameMap(); + + private static IReadOnlyDictionary GenerateSpawnerNameMap() + { + var result = new Dictionary(); + + result[FnvHash.HashFnv1a_64("hoge")] = "hoge"; + + var gimmicks = new[] { "no", "tree", "rock", "crystal", "snow", "box", "leaves_r", "leaves_g", "yachi" }; + foreach (var gimmick in gimmicks) + { + result[FnvHash.HashFnv1a_64($"{gimmick}")] = $"{gimmick}"; + for (var which = 0; which < 20; which++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}{which:00}")] = $"{gimmick}{which:00}"; + result[FnvHash.HashFnv1a_64($"{gimmick}_{which:00}")] = $"{gimmick}_{which:00}"; + for (var i = 0; i < 100; i++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{which:00}_{i:00}")] = $"{gimmick}_{which:00}_{i:00}"; + result[FnvHash.HashFnv1a_64($"{gimmick}_{which:00}_ex{i:00}")] = $"{gimmick}_{which:00}_ex{i:00}"; + for (var j = 0; j < 3; j++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{which:00}_{i:00}_{j:00}")] = $"{gimmick}_{which:00}_{i:00}_{j:00}"; + result[FnvHash.HashFnv1a_64($"{gimmick}_{which:00}_ex{i:00}_{j:00}")] = $"{gimmick}_{which:00}_ex{i:00}_{j:00}"; + for (var k = 0; k < 3; k++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{which:00}_{i:00}{j:00}_{k:00}")] = $"{gimmick}_{which:00}_{i:00}{j:00}_{k:00}"; + } + } + } + } + + + for (var w1 = 0; w1 < 5; w1++) + { + for (var w2 = 0; w2 < 5; w2++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{w1:00}_{w2:00}")] = $"{gimmick}_{w1:00}_{w2:00}"; + for (var i = 0; i < 100; i++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{w1:00}_{w2:00}_{i:00}")] = $"{gimmick}_{w1:00}_{w2:00}_{i:00}"; + for (var j = 0; j < 3; j++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{w1:00}_{w2:00}_{i:00}_{j:00}")] = $"{gimmick}_{w1:00}_{w2:00}_{i:00}_{j:00}"; + for (var k = 0; k < 3; k++) + { + result[FnvHash.HashFnv1a_64($"{gimmick}_{w1:00}_{w2:00}_{i:00}{j:00}_{k:00}")] = $"{gimmick}_{w1:00}_{w2:00}_{i:00}{j:00}_{k:00}"; + } + } + } + } + } + } + + return result; + } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LandmarkItemSpawn8a_F03 +{ + [FlatBufferItem(00)] public int Field_00 { get; set; } + [FlatBufferItem(01)] public int Field_01 { get; set; } + [FlatBufferItem(02)] public bool Field_02 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/LandmarkItemTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/LandmarkItemTable8a.cs new file mode 100644 index 00000000..2251b164 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/LandmarkItemTable8a.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; +using pkNX.Containers; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LandmarkItemTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public LandmarkItem8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LandmarkItem8a : ISlotTableConsumer +{ + [FlatBufferItem(00)] public string Field_00 { get; set; } = string.Empty; + [FlatBufferItem(01)] public ulong Field_01 { get; set; } + [FlatBufferItem(02)] public ulong LandmarkItemNameID { get; set; } + [FlatBufferItem(03)] public PlacementParameters8a[] Field_03 { get; set; } = Array.Empty(); + [FlatBufferItem(04)] public float Scalar { get; set; } + [FlatBufferItem(05)] public FlatDummyObject Field_05 { get; set; } = new(); + [FlatBufferItem(06)] public FlatDummyObject Field_06 { get; set; } = new(); + [FlatBufferItem(07)] public FlatDummyObject Field_07 { get; set; } = new(); + [FlatBufferItem(08)] public ulong LandmarkItemSpawnTableID { get; set; } + + public PlacementParameters8a Parameters + { + get { if (Field_03.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_03)}"); return Field_03[0]; } + set { if (Field_03.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_03)}"); Field_03[0] = value; } + } + + public bool UsesTable(ulong table) => LandmarkItemSpawnTableID == table; + + public IEnumerable GetIntersectingLocations(IReadOnlyList locations, float bias) + { + var c = Parameters.Coordinates; + return GetIntersectingLocations(locations, bias, c, Scalar + bias); + } + + private static IEnumerable GetIntersectingLocations(IReadOnlyList locations, float bias, PlacementV3f8a c, float scalar) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.IntersectsSphere(c.X, c.Y, c.Z, scalar)) + result.Add(loc); + } + + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } + + public IEnumerable GetContainingLocations(IReadOnlyList locations) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.Contains(Parameters.Coordinates)) + result.Add(loc); + } + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } + + public string NameSummary + { + get + { + var map = GetNameMap(); + if (map.TryGetValue(LandmarkItemNameID, out var name)) + return $"\"{name}\""; + + return $"0x{LandmarkItemNameID:X16}"; + } + } + + private static IReadOnlyDictionary? _landmarkItemNameMap; + private static IReadOnlyDictionary GetNameMap() => _landmarkItemNameMap ??= GenerateLandmarkItemNameMap(); + + private static IReadOnlyDictionary GenerateLandmarkItemNameMap() + { + var result = new Dictionary(); + + var gimmicks = new[] { "no", "tree", "rock", "crystal", "snow", "box", "leaves_r", "leaves_g", "yachi" }; + foreach (var gimmick in gimmicks) + { + result[FnvHash.HashFnv1a_64($"gimmick_{gimmick}")] = $"gimmick_{gimmick}"; + for (var which = 0; which < 20; which++) + { + result[FnvHash.HashFnv1a_64($"gimmick_{gimmick}{which:00}")] = $"gimmick_{gimmick}{which:00}"; + result[FnvHash.HashFnv1a_64($"gimmick_{gimmick}_{which:00}")] = $"gimmick_{gimmick}_{which:00}"; + } + } + + for (var which = 0; which < 20; which++) + { + result[FnvHash.HashFnv1a_64($"gimmick_tree{which:00}_snow")] = $"gimmick_tree{which:00}_snow"; + foreach (var color in new[] { "blue", "pink", "yellow" }) + { + result[FnvHash.HashFnv1a_64($"gimmick_tree{which:00}_{color}")] = $"gimmick_tree{which:00}_{color}"; + result[FnvHash.HashFnv1a_64($"gimmick_tree{which:00}_snow_{color}")] = $"gimmick_tree{which:00}_snow_{color}"; + } + } + + return result; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/LbPointsTable.cs b/pkNX.Structures.FlatBuffers/Arceus/LbPointsTable.cs new file mode 100644 index 00000000..c4a5499c --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/LbPointsTable.cs @@ -0,0 +1,31 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LbPointsTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public LbPointsInfo[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class LbPointsInfo +{ + [FlatBufferItem(00)] public int Field_00 { get; set; } + [FlatBufferItem(01)] public float Field_01 { get; set; } + [FlatBufferItem(02)] public float Field_02 { get; set; } + [FlatBufferItem(03)] public float Field_03 { get; set; } + [FlatBufferItem(04)] public int Field_04 { get; set; } + [FlatBufferItem(05)] public ulong Field_05 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Learnset8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Learnset8a.cs new file mode 100644 index 00000000..edfa092a --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Learnset8a.cs @@ -0,0 +1,51 @@ +using System; +using System.ComponentModel; +using System.IO; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Learnset8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public Learnset8aMeta[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Learnset8aMeta +{ + [FlatBufferItem(0)] public ushort Species { get; set; } + [FlatBufferItem(1)] public ushort Form { get; set; } + [FlatBufferItem(2)] public Learnset8aEntry[] Mainline { get; set; } = Array.Empty(); + [FlatBufferItem(3)] public Learnset8aEntry[] Arceus { get; set; } = Array.Empty(); + + public byte[] WriteAsLearn6() + { + using var ms = new MemoryStream(); + using var br = new BinaryWriter(ms); + foreach (var entry in Arceus) + { + br.Write(entry.Move); + br.Write(entry.Level); + } + br.Write(-1); + return ms.ToArray(); + } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Learnset8aEntry +{ + [FlatBufferItem(0)] public ushort Level { get; set; } + [FlatBufferItem(1)] public ushort LevelMaster { get; set; } + [FlatBufferItem(2)] public ushort Move { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/MassOutbreakTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/MassOutbreakTable8a.cs new file mode 100644 index 00000000..d84ee359 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/MassOutbreakTable8a.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class MassOutbreakTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public MassOutbreak8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class MassOutbreak8a : IFlatBufferArchive +{ + [FlatBufferItem(0)] public ulong Hash { get; set; } + [FlatBufferItem(1)] public int Field_01 { get; set; } + [FlatBufferItem(2)] public MassOutbreakInfo8a[] Table { get; set; } = Array.Empty(); + [FlatBufferItem(3)] public string Field_03 { get; set; } = string.Empty; + [FlatBufferItem(4)] public int Field_04 { get; set; } + [FlatBufferItem(5)] public int Field_05 { get; set; } + [FlatBufferItem(6)] public int Field_06 { get; set; } + [FlatBufferItem(7)] public int Field_07 { get; set; } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class MassOutbreakInfo8a +{ + [FlatBufferItem(0)] public ulong Hash_00 { get; set; } + [FlatBufferItem(1)] public ulong Hash_01 { get; set; } + [FlatBufferItem(2)] public string Field_02 { get; set; } = string.Empty; + [FlatBufferItem(3)] public string Field_03 { get; set; } = string.Empty; + [FlatBufferItem(4)] public string Field_04 { get; set; } = string.Empty; + [FlatBufferItem(5)] public string Field_05 { get; set; } = string.Empty; + [FlatBufferItem(6)] public string Field_06 { get; set; } = string.Empty; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/MoveShopTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/MoveShopTable8a.cs new file mode 100644 index 00000000..4c21ed75 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/MoveShopTable8a.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class MoveShopTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public MoveShopIndex[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class MoveShopIndex +{ + [FlatBufferItem(0)] public int Index { get; set; } + [FlatBufferItem(1)] public int Move { get; set; } + [FlatBufferItem(2)] public int Price { get; set; } + [FlatBufferItem(3)] public ulong Hash { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PersonalInfoLAfb.cs b/pkNX.Structures.FlatBuffers/Arceus/PersonalInfoLAfb.cs new file mode 100644 index 00000000..dd049f75 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PersonalInfoLAfb.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using FlatSharp.Attributes; +using System.Linq; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +/// +/// class with values from the games. +/// +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PersonalTableLA : IFlatBufferArchive +{ + [FlatBufferItem(0)] public PersonalInfoLAfb[] Table { get; set; } = Array.Empty(); +} + +/// +/// class with values from the games. +/// +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PersonalInfoLAfb +{ + [FlatBufferItem(00)] public ushort Species { get; set; } // ushort + [FlatBufferItem(01)] public ushort Form { get; set; } // ushort + [FlatBufferItem(02)] public byte IsPresentInGame { get; set; } // byte + [FlatBufferItem(03)] public byte Type1 { get; set; } // byte + [FlatBufferItem(04)] public byte Type2 { get; set; } // byte + [FlatBufferItem(05)] public ushort Ability1 { get; set; } // ushort + [FlatBufferItem(06)] public ushort Ability2 { get; set; } // ushort + [FlatBufferItem(07)] public ushort AbilityH { get; set; } // ushort + [FlatBufferItem(08)] public byte Stat_HP { get; set; } // byte + [FlatBufferItem(09)] public byte Stat_ATK { get; set; } // byte + [FlatBufferItem(10)] public byte Stat_DEF { get; set; } // byte + [FlatBufferItem(11)] public byte Stat_SPA { get; set; } // byte + [FlatBufferItem(12)] public byte Stat_SPD { get; set; } // byte + [FlatBufferItem(13)] public byte Stat_SPE { get; set; } // byte + [FlatBufferItem(14)] public byte Gender { get; set; } // byte + [FlatBufferItem(15)] public byte EXPGrowth { get; set; } // byte + [FlatBufferItem(16)] public byte EvoStage { get; set; } // byte + [FlatBufferItem(17)] public byte CatchRate{ get; set; } // byte + [FlatBufferItem(18)] public byte Field_18 { get; set; } // Always Default (0) + [FlatBufferItem(19)] public byte Color { get; set; } // byte + [FlatBufferItem(20)] public ushort Height { get; set; } // ushort + [FlatBufferItem(21)] public ushort Weight { get; set; } // ushort + [FlatBufferItem(22)] public uint TM_A { get; set; } // uint, not used by game + [FlatBufferItem(23)] public uint TM_B { get; set; } // uint, not used by game + [FlatBufferItem(24)] public uint TM_C { get; set; } // uint, not used by game + [FlatBufferItem(25)] public uint TM_D { get; set; } // uint, not used by game + [FlatBufferItem(26)] public uint TR_A { get; set; } // uint, not used by game + [FlatBufferItem(27)] public uint TR_B { get; set; } // uint, not used by game + [FlatBufferItem(28)] public uint TR_C { get; set; } // uint, not used by game + [FlatBufferItem(29)] public uint TR_D { get; set; } // uint, not used by game + [FlatBufferItem(30)] public uint TypeTutor { get; set; } // uint, not used by game + [FlatBufferItem(31)] public ushort BaseEXP { get; set; } // ushort + [FlatBufferItem(32)] public byte EV_HP { get; set; } // byte + [FlatBufferItem(33)] public byte EV_ATK { get; set; } // byte + [FlatBufferItem(34)] public byte EV_DEF { get; set; } // byte + [FlatBufferItem(35)] public byte EV_SPA { get; set; } // byte + [FlatBufferItem(36)] public byte EV_SPD { get; set; } // byte + [FlatBufferItem(37)] public byte EV_SPE { get; set; } // byte + [FlatBufferItem(38)] public ushort Item1 { get; set; } // ushort + [FlatBufferItem(39)] public ushort Item2 { get; set; } // ushort + [FlatBufferItem(40)] public ushort Item3 { get; set; } // Always Default (0) + [FlatBufferItem(41)] public byte EggGroup1 { get; set; } // byte + [FlatBufferItem(42)] public byte EggGroup2 { get; set; } // byte + [FlatBufferItem(43)] public ushort HatchSpecies { get; set; } // ushort + [FlatBufferItem(44)] public ushort LocalFormIndex { get; set; } // ushort + [FlatBufferItem(45)] public byte Field_45 { get; set; } // byte + [FlatBufferItem(46)] public ushort Field_46 { get; set; } // ushort + [FlatBufferItem(47)] public byte Field_47 { get; set; } // byte + [FlatBufferItem(48)] public byte BaseFriendship { get; set; } // byte + [FlatBufferItem(49)] public ushort DexIndexHisui { get; set; } // ushort + [FlatBufferItem(50)] public ushort DexIndexOther { get; set; } // ushort + [FlatBufferItem(51)] public int DexIndexLocal1 { get; set; } // uint + [FlatBufferItem(52)] public int DexIndexLocal2 { get; set; } // uint + [FlatBufferItem(53)] public int DexIndexLocal3 { get; set; } // uint + [FlatBufferItem(54)] public int DexIndexLocal4 { get; set; } // uint + [FlatBufferItem(55)] public int DexIndexLocal5 { get; set; } // uint + [FlatBufferItem(56)] public uint MoveShop1 { get; set; } // uint + [FlatBufferItem(57)] public uint MoveShop2 { get; set; } // uint +} + +public static class PersonalConverter +{ + public static byte[] GetBin(PersonalTableLA table) + { + var all = FromArceus(table); + var data = all.SelectMany(z => z.Write()).ToArray(); + return data; + } + + public static PersonalInfoLA[] FromArceus(PersonalTableLA table) + { + var max = table.Table.Max(z => z.Species); + var baseForms = new PersonalInfoLA[max + 1]; + var formTable = new List(); + + for (int i = 0; i <= max; i++) + { + var forms = table.Table.Where(z => z.Species == (ushort)i).OrderBy(z => z.Form).ToList(); + + var e = forms[0]; + baseForms[i] = GetObj(e, forms, max, formTable); + for (int f = 1; f < forms.Count; f++) + formTable.Add(GetObj(forms[f], forms, max, formTable, f)); + } + + return baseForms.Concat(formTable).ToArray(); + } + + // ugly converter to be a data-backed object + private static PersonalInfoLA GetObj(PersonalInfoLAfb e, List forms, ushort max, List formTable, int f = 0) + { + var result = new PersonalInfoLA(new byte[PersonalInfoLA.SIZE]) + { + HP = e.Stat_HP, + ATK = e.Stat_ATK, + DEF = e.Stat_DEF, + SPA = e.Stat_SPA, + SPD = e.Stat_SPD, + SPE = e.Stat_SPE, + FormeCount = forms.Count, + Type1 = e.Type1, + Type2 = e.Type2, + Gender = e.Gender, + Ability1 = e.Ability1, + Ability2 = e.Ability2, + AbilityH = e.AbilityH, + IsPresentInGame = e.IsPresentInGame == 1, + Item1 = e.Item1, + Item2 = e.Item2, + Height = e.Height, + Weight = e.Weight, + EggGroup1 = e.EggGroup1, + EggGroup2 = e.EggGroup2, + EvoStage = e.EvoStage, + BaseFriendship = e.BaseFriendship, + EXPGrowth = e.EXPGrowth, + HatchSpecies = e.HatchSpecies, + LocalFormIndex = e.LocalFormIndex, + EV_HP = e.EV_HP, + EV_ATK = e.EV_ATK, + EV_DEF = e.EV_DEF, + EV_SPA = e.EV_SPA, + EV_SPD = e.EV_SPD, + EV_SPE = e.EV_SPE, + CatchRate = e.CatchRate, + DexIndexHisui = e.DexIndexHisui, + DexIndexLocal1 = e.DexIndexLocal1, + DexIndexLocal2 = e.DexIndexLocal2, + DexIndexLocal3 = e.DexIndexLocal3, + DexIndexLocal4 = e.DexIndexLocal4, + DexIndexLocal5 = e.DexIndexLocal5, + Color = e.Color, + BaseEXP = e.BaseEXP, + + Species = e.Species, + Form = e.Form, + }; + result.SetFormStat(f != 0 ? 0 : forms.Count == 1 ? 0 : max + formTable.Count + 1); + + var shop = e.MoveShop1 | ((ulong)e.MoveShop2 << 32); + bool[] flags = new bool[64]; + for (int i = 0; i < flags.Length; i++) + flags[i] = (shop & (1ul << i)) != 0; + result.SpecialTutors = new[] { flags }; + + BitConverter.GetBytes(e.TM_A).CopyTo(result.Data, 0x28); + BitConverter.GetBytes(e.TM_B).CopyTo(result.Data, 0x2C); + BitConverter.GetBytes(e.TM_C).CopyTo(result.Data, 0x30); + BitConverter.GetBytes(e.TM_D).CopyTo(result.Data, 0x34); + BitConverter.GetBytes(e.TypeTutor).CopyTo(result.Data, 0x38); + BitConverter.GetBytes(e.TR_A).CopyTo(result.Data, 0x3C); + BitConverter.GetBytes(e.TR_B).CopyTo(result.Data, 0x40); + BitConverter.GetBytes(e.TR_C).CopyTo(result.Data, 0x44); + BitConverter.GetBytes(e.TR_D).CopyTo(result.Data, 0x48); + result.LoadTMHM(); + result.LoadTutors(); + + return result; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs new file mode 100644 index 00000000..25d3a083 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs @@ -0,0 +1,66 @@ +using pkNX.Containers; +using System; +using System.Collections.Generic; + +namespace pkNX.Structures.FlatBuffers; + +// Not a flatbuffer; wraps the fields into a single object. +public sealed class AreaInstance8a +{ + public readonly string AreaName; + + public readonly PlacementLocation8a[] Locations; + public readonly PlacementSpawner8a[] Spawners; + public readonly PlacementSpawner8a[] Wormholes; + public readonly LandmarkItemSpawn8a[] LandItems; + public readonly LandmarkItem8a[] LandMarks; + public readonly PlacementUnnnEntry[] Unown; + public readonly PlacementMkrgEntry[] Mikaruge; + + public readonly EncounterDataArchive8a Encounters; + + public readonly AreaInstance8a? ParentArea; + public readonly List SubAreas; + + public bool IsSubArea => ParentArea != null; + + private AreaInstance8a(GFPack resident, string areaName, AreaInstance8a? parentArea) + { + var f = resident.GetDataFullPath($"bin/encount/{areaName}/encount_data.bin"); + Encounters = FlatBufferConverter.DeserializeFrom(f); + + var locationf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/location/location.bin"); + var spawnerf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/spawner/spawner_data.bin"); + var wh_spawnerf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/wh_spawner/spawner_data.bin"); + var l_spawnerf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/landmark_item/expansion/landmark_item_spawn_table.bin"); + var l_markf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/landmark_item/landmark_item.bin"); + var unnf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/unnn/unnn.bin"); + var mkrgf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/mkrg/mkrg.bin"); + + Locations = FlatBufferConverter.DeserializeFrom(locationf).Table; + Spawners = FlatBufferConverter.DeserializeFrom(spawnerf).Table; + Wormholes = FlatBufferConverter.DeserializeFrom(wh_spawnerf).Table; + LandItems = FlatBufferConverter.DeserializeFrom(l_spawnerf).Table; + LandMarks = FlatBufferConverter.DeserializeFrom(l_markf).Table; + Unown = FlatBufferConverter.DeserializeFrom(unnf).Table; + Mikaruge = FlatBufferConverter.DeserializeFrom(mkrgf).Table; + + AreaName = areaName; + + ParentArea = parentArea; + SubAreas = new List(); + } + + public static AreaInstance8a Create(GFPack resident, string[] areaNameList) + { + if (areaNameList.Length < 1) + throw new ArgumentException("Invalid area name list!"); + + var parentArea = new AreaInstance8a(resident, areaNameList[0], null); + + for (var i = 1; i < areaNameList.Length; i++) + parentArea.SubAreas.Add(new AreaInstance8a(resident, areaNameList[i], parentArea)); + + return parentArea; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/OybnSettingTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/OybnSettingTable8a.cs new file mode 100644 index 00000000..0e2e29b4 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/OybnSettingTable8a.cs @@ -0,0 +1,31 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class OybnSettingTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public OybnSetting8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class OybnSetting8a +{ + [FlatBufferItem(00)] public ulong Hash { get; set; } + [FlatBufferItem(01)] public int Field_01 { get; set; } + [FlatBufferItem(02)] public int Field_02 { get; set; } + [FlatBufferItem(03)] public int Field_03 { get; set; } + [FlatBufferItem(04)] public int Field_04 { get; set; } + [FlatBufferItem(05)] public int Field_05 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementItemArchive8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementItemArchive8a.cs new file mode 100644 index 00000000..86ff2996 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementItemArchive8a.cs @@ -0,0 +1,51 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementItemArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlacementItem8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementItem8a +{ + [FlatBufferItem(00)] public string Field_00 { get; set; } = string.Empty; + [FlatBufferItem(01)] public ulong Field_01 { get; set; } + [FlatBufferItem(02)] public ulong Field_02 { get; set; } + [FlatBufferItem(03)] public PlacementItem8a_F03[] Field_03 { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementItem8a_F03 +{ + [FlatBufferItem(00)] public byte Field_00 { get; set; } + [FlatBufferItem(01)] public byte Field_01 { get; set; } + [FlatBufferItem(02)] public string Field_02 { get; set; } = string.Empty; + [FlatBufferItem(03)] public string Field_03 { get; set; } = string.Empty; + [FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; + [FlatBufferItem(05)] public string Field_05 { get; set; } = string.Empty; + [FlatBufferItem(06)] public string Field_06 { get; set; } = string.Empty; + [FlatBufferItem(07)] public PlacementV3f8a Field_07 { get; set; } = new(); + [FlatBufferItem(08)] public PlacementItem8a_F08 Field_08 { get; set; } = new(); + [FlatBufferItem(09)] public PlacementV3f8a Field_09 { get; set; } = new(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementItem8a_F08 +{ + [FlatBufferItem(00)] public byte Field_00 { get; set; } // unk + [FlatBufferItem(01)] public float Field_01 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementLocation8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementLocation8a.cs new file mode 100644 index 00000000..0c6bdaf9 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementLocation8a.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Numerics; +using FlatSharp.Attributes; +using pkNX.Containers; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementLocation8a +{ + public const ulong ShapeIdSphere = 0x645FB59E96A6F998; + public const ulong ShapeIdBox = 0xE6B50E272D8CD992; + public const ulong ShapeNone = 0xCBF29CE484222645; + + [FlatBufferEnum(typeof(ulong))] + public enum LocationType8a : ulong + { + Bgm = 0xD090DB268FBCE531, // "Bgm" + BuddyInfeasibleOcclusion = 0x1713B588A00FF9F2, // "BuddyInfeasibleOcclusion" + Evolution = 0x51550D70709EA65E, // "Evolution" + LightOcclusion = 0x94FEC9F97E988350, // "LightOcclusion" + NoRespawnOcclusion = 0xD91EE555A7E6DC07, // "NoRespawnOcclusion" + OcclusionSpace = 0x613F7FD830366C6C, // "OcclusionSpace" + PlaceName = 0x0351C91B3FD84C97, // "PlaceName + RideInfeasibleOcclusion = 0x677D529DBB999AD6, // "RideInfeasibleOcclusion" + SafetyArea = 0x4C62CAA0FF9B445A, // "SafetyArea" + SaveGameOver = 0x77EEDD438D48D1B8, // "SaveGameOver" + TakeOffShoesOcclusion = 0x09FBFD6B423FAEA6, // "TakeOffShoesOcclusion" + TreeCullingFrustumFar = 0xD55673931A300690, // "TreeCullingFrustumFar" + WeatherOcclusion = 0x2B3435F7DF6CF454, // "WeatherOcclusion" + }; + + [FlatBufferItem(0)] public string Field_00 { get; set; } = string.Empty; + [FlatBufferItem(1)] public ulong Field_01 { get; set; } + [FlatBufferItem(2)] public PlacementParameters8a[] ParameterSet { get; set; } = { new() }; + [FlatBufferItem(3)] public ulong ShapeID { get; set; } + [FlatBufferItem(4)] public float SizeX { get; set; } + [FlatBufferItem(5)] public float SizeY { get; set; } + [FlatBufferItem(6)] public float SizeZ { get; set; } + [FlatBufferItem(7)] public ulong LocationTypeID { get; set; } + [FlatBufferItem(8)] public string LocationTypeArg1 { get; set; } = string.Empty; + [FlatBufferItem(9)] public ulong LocationTypeArg2 { get; set; } + + public PlacementParameters8a Parameters + { + get { if (ParameterSet.Length != 1) throw new ArgumentException($"Invalid {nameof(ParameterSet)}"); return ParameterSet[0]; } + set { if (ParameterSet.Length != 1) throw new ArgumentException($"Invalid {nameof(ParameterSet)}"); ParameterSet[0] = value; } + } + + public bool IsNamedPlace => LocationTypeID == 0x0351C91B3FD84C97; + public string PlaceName => LocationTypeArg1; + + public string ShapeSummary => ShapeID switch + { + ShapeIdSphere => "/* Shape = */ \"sphere\"", + ShapeIdBox => "/* Shape = */ \"box\"", + ShapeNone => "/* Shape = */ \"\"", + _ => $"/* Shape = */ 0x{ShapeID:X16}", + }; + + public bool Contains(float x, float y, float z) + { + if (ShapeID == ShapeIdSphere) + { + if (!Parameters.Scale.IsDefault) + throw new NotImplementedException("Scaled spheres not yet supported!"); + + var radius = Math.Abs(SizeX); + var distance = Parameters.Coordinates.DistanceTo(x, y, z); + return distance <= radius; + } + if (ShapeID == ShapeIdBox) + { + // Get the details of the box + var center = new Vector3(Parameters.Coordinates.X, Parameters.Coordinates.Y, Parameters.Coordinates.Z); + var rotation = Quaternion.CreateFromYawPitchRoll(Parameters.Rotation.Y * (float)Math.PI / 180.0f, Parameters.Rotation.X * (float)Math.PI / 180.0f, Parameters.Rotation.Z * (float)Math.PI / 180.0f); + var inverseRotation = Quaternion.Conjugate(rotation); + + // Get the distance to the point along non-rotated axes + var delta = Vector3.Transform(new Vector3(x, y, z) - center, inverseRotation); + + var x_unit = Parameters.Scale.X * SizeX / 2; + var y_unit = Parameters.Scale.Y * SizeY / 2; + var z_unit = Parameters.Scale.Z * SizeZ / 2; + + // Check that the point is within unit-distance along all axes + if (Math.Abs(delta.X) > Math.Abs(x_unit)) + return false; + if (Math.Abs(delta.Y) > Math.Abs(y_unit)) + return false; + if (Math.Abs(delta.Z) > Math.Abs(z_unit)) + return false; + + return true; + } + { + if (ShapeID != ShapeNone) // "" + throw new NotImplementedException("Unknown ShapeID!"); + return false; + } + } + + public bool IntersectsSphere(float x, float y, float z, float sphereRadius) + { + if (sphereRadius == 0) + return Contains(x, y, z); + + if (ShapeID == ShapeIdSphere) + { + if (!Parameters.Scale.IsDefault) + throw new NotImplementedException("Scaled spheres not yet supported!"); + + var radius = Math.Abs(SizeX); + var distance = Parameters.Coordinates.DistanceTo(x, y, z); + return distance <= radius + sphereRadius; + } + if (ShapeID == ShapeIdBox) + { + // Get the details of the box + var center = new Vector3(Parameters.Coordinates.X, Parameters.Coordinates.Y, Parameters.Coordinates.Z); + var rotation = Quaternion.CreateFromYawPitchRoll(Parameters.Rotation.Y * (float)Math.PI / 180.0f, Parameters.Rotation.X * (float)Math.PI / 180.0f, Parameters.Rotation.Z * (float)Math.PI / 180.0f); + var inverseRotation = Quaternion.Conjugate(rotation); + + // Get the distance to the point along non-rotated axes + var sphereCenter = new Vector3(x, y, z); + var delta = Vector3.Transform(sphereCenter - center, inverseRotation); + + var x_unit = Parameters.Scale.X * SizeX / 2; + var y_unit = Parameters.Scale.Y * SizeY / 2; + var z_unit = Parameters.Scale.Z * SizeZ / 2; + + var contains = true; + + if (Math.Abs(delta.X) > Math.Abs(x_unit)) + { + contains = false; + delta.X = Math.Abs(x_unit) * Math.Sign(delta.X); + } + + if (Math.Abs(delta.Y) > Math.Abs(y_unit)) + { + contains = false; + delta.Y = Math.Abs(y_unit) * Math.Sign(delta.Y); + } + + if (Math.Abs(delta.Z) > Math.Abs(z_unit)) + { + contains = false; + delta.Z = Math.Abs(z_unit) * Math.Sign(delta.Z); + } + + // If the point is contained, it necessarily intersects + if (contains) + return true; + + // Get the point on the box closest to the sphere + var closest = center + Vector3.Transform(delta, rotation); + + // Check if closest point on box is within sphere's radius + var distance = Vector3.Distance(closest, sphereCenter); + return distance <= sphereRadius; + } + { + if (ShapeID != ShapeNone) // "" + throw new NotImplementedException("Unknown ShapeID!"); + return false; + } + } + + public bool Contains(PlacementV3f8a v) => Contains(v.X, v.Y, v.Z); + + public string LocationTypeSummary + { + get + { + if (LocationTypeArg2 != 0xCBF29CE484222645) + return $"{LocationTypeIdSummary}(\"{LocationTypeArg1}\", {LocationArg2Summary})"; + + if (!string.IsNullOrEmpty(LocationTypeArg1)) + return $"{LocationTypeIdSummary}(\"{LocationTypeArg1}\")"; + + return LocationTypeIdSummary; + } + } + + public string LocationTypeIdSummary + { + get + { + if (!Enum.IsDefined(typeof(LocationType8a), LocationTypeID)) + throw new ArgumentOutOfRangeException(nameof(LocationTypeID), LocationTypeID, $"Unknown 0x{LocationTypeID:X16}."); + return Enum.GetName(typeof(LocationType8a), LocationTypeID); + } + } + + // lazy init + private static IReadOnlyDictionary? _locationArgMap; + private static IReadOnlyDictionary GetLocationArgMap() => _locationArgMap ??= GenerateLocationArgMap(); + + private static IReadOnlyDictionary GenerateLocationArgMap() + { + var result = new Dictionary(); + result[0xCBF29CE484222645] = ""; + + // PlaceName + result[FnvHash.HashFnv1a_64("NoneReport")] = "NoneReport"; + + // Bgm + result[FnvHash.HashFnv1a_64("LOW")] = "LOW"; + result[FnvHash.HashFnv1a_64("HIGH")] = "HIGH"; + return result; + } + + public string LocationArg2Summary => GetLocationArgMap().TryGetValue(LocationTypeArg2, out var arg) ? $"\"{arg}\"" : $"0x{LocationTypeArg2:X16}"; + + public override string ToString() => $"Location(\"{Field_00}\", 0x{Field_01:X16}, {Parameters}, {ShapeSummary}, {SizeX}, {SizeY}, {SizeZ}, {LocationTypeSummary})"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementLocationArchive8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementLocationArchive8a.cs new file mode 100644 index 00000000..0444858b --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementLocationArchive8a.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementLocationArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlacementLocation8a[] Table { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementMkrgTable.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementMkrgTable.cs new file mode 100644 index 00000000..d0451a97 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementMkrgTable.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementMkrgTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlacementMkrgEntry[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementMkrgEntry +{ + [FlatBufferItem(0)] public string Identifier { get; set; } = string.Empty; + [FlatBufferItem(1)] public ulong Hash_01 { get; set; } + [FlatBufferItem(2)] public PlacementParameters8a[] Field_02 { get; set; } = Array.Empty(); + [FlatBufferItem(3)] public float Field_03 { get; set; } + [FlatBufferItem(4)] public float Field_04 { get; set; } + + public PlacementParameters8a Parameters + { + get { if (Field_02.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_02)}"); return Field_02[0]; } + set { if (Field_02.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_02)}"); Field_02[0] = value; } + } + + public override string ToString() => $"Mikaruge(\"{Identifier}\", 0x{Hash_01:X16}, {Parameters}, {Field_03}, {Field_04})"; + + public IEnumerable GetContainingLocations(IReadOnlyList locations) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.Contains(Parameters.Coordinates)) + result.Add(loc); + } + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementParameters8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementParameters8a.cs new file mode 100644 index 00000000..dee0330c --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementParameters8a.cs @@ -0,0 +1,28 @@ +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementParameters8a : IHasCondition8a +{ + [FlatBufferItem(0)] public ConditionType8a ConditionTypeID { get; set; } + [FlatBufferItem(1)] public Condition8a ConditionID { get; set; } + [FlatBufferItem(2)] public string ConditionArg1 { get; set; } = string.Empty; + [FlatBufferItem(3)] public string ConditionArg2 { get; set; } = string.Empty; + [FlatBufferItem(4)] public string ConditionArg3 { get; set; } = string.Empty; + [FlatBufferItem(5)] public string ConditionArg4 { get; set; } = string.Empty; + [FlatBufferItem(6)] public string ConditionArg5 { get; set; } = string.Empty; + [FlatBufferItem(7)] public PlacementV3f8a Coordinates { get; set; } = new(); + [FlatBufferItem(8)] public PlacementV3f8a Rotation { get; set; } = new(); + [FlatBufferItem(9)] public PlacementV3f8a Scale { get; set; } = new(); + + public override string ToString() => $"PlacementParameters(/* ConditionTypeID = */ {this.GetConditionTypeSummary()}, /* Condition = */ {this.GetConditionSummary()}, {Coordinates}, {Rotation}, {Scale})"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawner8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawner8a.cs new file mode 100644 index 00000000..43a28568 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawner8a.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; +using pkNX.Containers; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementSpawner8a : ISlotTableConsumer +{ + [FlatBufferItem(0)] public ulong SpawnerID { get; set; } + [FlatBufferItem(1)] public ulong Field_01 { get; set; } + [FlatBufferItem(2)] public PlacementParameters8a[] Field_02 { get; set; } = { new() }; + [FlatBufferItem(3)] public string Field_03 { get; set; } = string.Empty; + [FlatBufferItem(4)] public float Scalar { get; set; } + [FlatBufferItem(5)] public PlacementV3f8a Field_05 { get; set; } = new(); + [FlatBufferItem(6)] public PlacementV3f8a Field_06 { get; set; } = new(); + [FlatBufferItem(7)] public int Field_07 { get; set; } + [FlatBufferItem(8)] public int Field_08 { get; set; } + [FlatBufferItem(9)] public int Field_09 { get; set; } + [FlatBufferItem(10)] public bool Field_10 { get; set; } + [FlatBufferItem(11)] public bool Field_11 { get; set; } + [FlatBufferItem(12)] public bool Field_12 { get; set; } + [FlatBufferItem(13)] public ulong GroupID { get; set; } + [FlatBufferItem(14)] public float Field_14 { get; set; } + [FlatBufferItem(15)] public float Field_15 { get; set; } + [FlatBufferItem(16)] public int Field_16 { get; set; } + [FlatBufferItem(17)] public float Field_17 { get; set; } + [FlatBufferItem(18)] public float Field_18 { get; set; } + [FlatBufferItem(19)] public float Field_19 { get; set; } + [FlatBufferItem(20)] public PlacementSpawnerF208a[] Field_20 { get; set; } = { new() }; + [FlatBufferItem(21)] public PlacementSpawnerF218a[] Field_21 { get; set; } = { new() }; + [FlatBufferItem(22)] public string[] Field_22 { get; set; } = { string.Empty }; + + public PlacementParameters8a Parameters + { + get { if (Field_02.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_02)}"); return Field_02[0]; } + set { if (Field_02.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_02)}"); Field_02[0] = value; } + } + + public PlacementSpawnerF208a Field_20_Value + { + get { if (Field_20.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_20)}"); return Field_20[0]; } + set { if (Field_20.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_20)}"); Field_20[0] = value; } + } + + public PlacementSpawnerF218a Field_21_Value + { + get { if (Field_21.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_21)}"); return Field_21[0]; } + set { if (Field_21.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_21)}"); Field_21[0] = value; } + } + + public string Field_22_Value + { + get { if (Field_22.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_22)}"); return Field_22[0]; } + set { if (Field_22.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_22)}"); Field_22[0] = value; } + } + + public IEnumerable GetIntersectingLocations(IReadOnlyList locations, float bias) + { + var c = Parameters.Coordinates; + return GetIntersectingLocations(locations, bias, c, Scalar + bias); + } + + private static IEnumerable GetIntersectingLocations(IReadOnlyList locations, float bias, PlacementV3f8a c, float scalar) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.IntersectsSphere(c.X, c.Y, c.Z, scalar)) + result.Add(loc); + } + + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } + + public IEnumerable GetContainingLocations(IReadOnlyList locations) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.Contains(Parameters.Coordinates)) + result.Add(loc); + } + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } + + private static IReadOnlyDictionary? _spawnerNameMap; + private static IReadOnlyDictionary GetSpawnerNameMap() => _spawnerNameMap ??= GenerateSpawnerNameMap(); + + private static IReadOnlyDictionary GenerateSpawnerNameMap() + { + var result = new Dictionary(); + + for (var i = 0; i < 10000; i++) + { + result[FnvHash.HashFnv1a_64($"{i:0000}")] = $"{i:0000}"; + result[FnvHash.HashFnv1a_64($"1{i:0000}")] = $"1{i:0000}"; + result[FnvHash.HashFnv1a_64($"02{i:0000}")] = $"02{i:0000}"; + result[FnvHash.HashFnv1a_64($"03{i:0000}")] = $"03{i:0000}"; + result[FnvHash.HashFnv1a_64($"4{i:0000}")] = $"4{i:0000}"; + result[FnvHash.HashFnv1a_64($"5{i:0000}")] = $"5{i:0000}"; + result[FnvHash.HashFnv1a_64($"ev{i:0000}")] = $"ev{i:0000}"; + result[FnvHash.HashFnv1a_64($"ex_{i:0000}")] = $"ex_{i:0000}"; + result[FnvHash.HashFnv1a_64($"eve_ex_{i:0000}")] = $"eve_ex_{i:0000}"; + + result[FnvHash.HashFnv1a_64($"huge_{i:0000}")] = $"huge_{i:0000}"; + + for (var j = 0; j < 20; j++) + { + result[FnvHash.HashFnv1a_64($"{i:0000}_{j:00}")] = $"{i:0000}_{j:00}"; + } + } + + for (var i = 0; i < 100; i++) + { + result[FnvHash.HashFnv1a_64($"area00_{i:00}")] = $"area00_{i:00}"; + result[FnvHash.HashFnv1a_64($"poke{i:00}")] = $"poke{i:00}"; + result[FnvHash.HashFnv1a_64($"sky{i:00}")] = $"sky{i:00}"; + result[FnvHash.HashFnv1a_64($"lnd_no_{i:00}")] = $"lnd_no_{i:00}"; + result[FnvHash.HashFnv1a_64($"sky_{i:00}")] = $"sky_{i:00}"; + result[FnvHash.HashFnv1a_64($"ex_mkrg_{i:00}")] = $"ex_mkrg_{i:00}"; + result[FnvHash.HashFnv1a_64($"ex_unnn_{i:00}")] = $"ex_unnn_{i:00}"; + } + + result[FnvHash.HashFnv1a_64($"ha_area01_s01_ev001")] = $"ha_area01_s01_ev001"; + result[FnvHash.HashFnv1a_64($"ha_area02_s02_ev001")] = $"ha_area01_s02_ev001"; + result[FnvHash.HashFnv1a_64($"ha_area02_s02_ev002")] = $"ha_area01_s02_ev002"; + result[FnvHash.HashFnv1a_64($"ha_area03_s03_ev001")] = $"ha_area03_s03_ev001"; + result[FnvHash.HashFnv1a_64($"ha_area04_ev001")] = $"ha_area04_ev001"; + result[FnvHash.HashFnv1a_64($"ha_area05_s03_ev001")] = $"ha_area05_s03_ev001"; + + result[FnvHash.HashFnv1a_64($"area03_s04_ev001")] = $"area03_s04_ev001"; + result[FnvHash.HashFnv1a_64($"area03_s04_ev002")] = $"area03_s04_ev002"; + result[FnvHash.HashFnv1a_64($"area03_s04_ev003")] = $"area03_s04_ev003"; + result[FnvHash.HashFnv1a_64($"area03_s04_ev004")] = $"area03_s04_ev004"; + result[FnvHash.HashFnv1a_64($"area03_s04_ev005")] = $"area03_s04_ev005"; + + for (var wh = 1; wh < 8; wh++) + { + for (var sub = 1; sub < 4; sub++) + { + for (var n = 1; n < 7; n++) + { + result[FnvHash.HashFnv1a_64($"main_whSpawner{wh:00}{sub:00}_{n:00}")] = $"main_whSpawner{wh:00}{sub:00}_{n:00}"; + result[FnvHash.HashFnv1a_64($"sub_whSpawner{wh:00}{sub:00}_{n:00}")] = $"sub_whSpawner{wh:00}{sub:00}_{n:00}"; + } + } + } + + return result; + } + + public string NameSummary + { + get + { + var map = GetSpawnerNameMap(); + if (map.TryGetValue(SpawnerID, out var name)) + return $"\"{name}\""; + + return $"0x{SpawnerID:X16}"; + } + } + + // lazy init + private static IReadOnlyDictionary? _groupNameMap; + private static IReadOnlyDictionary GetGroupNameMap() => _groupNameMap ??= GenerateGroupNameMap(); + + private static IReadOnlyDictionary GenerateGroupNameMap() + { + var result = new Dictionary(); + for (var i = 0; i < 100; i++) + { + var reg = $"grp{i:00}"; + var und = $"grp_{i:00}"; + result[FnvHash.HashFnv1a_64(und)] = und; + result[FnvHash.HashFnv1a_64(reg)] = reg; + } + result[0xCBF29CE484222645] = ""; + return result; + } + + public string GroupSummary + { + get + { + var map = GetGroupNameMap(); + if (map.TryGetValue(GroupID, out var name)) + return $"\"{name}\""; + + return $"0x{GroupID:X16}"; + } + } + + public override string ToString() => $"Spawner({NameSummary}, 0x{Field_01:X16}, {Parameters}, \"{Field_03}\", {Scalar}, {Field_05}, {Field_06}, {Field_07}, {Field_08}, {Field_09}, {Field_10}, {Field_11}, {Field_12}, /* Group = */ {GroupSummary}, {Field_14}, {Field_15}, {Field_16}, {Field_17}, {Field_18}, {Field_19}, {Field_20_Value}, {Field_21_Value}, {Field_22_Value})"; + + public bool UsesTable(ulong table) => Field_20_Value.EncounterTableID == table; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerData8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerData8a.cs new file mode 100644 index 00000000..0e6f526a --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerData8a.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementSpawnerArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlacementSpawner8a[] Table { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerF208a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerF208a.cs new file mode 100644 index 00000000..e7f5e18d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerF208a.cs @@ -0,0 +1,28 @@ +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementSpawnerF208a : IHasCondition8a +{ + [FlatBufferItem(0)] public ulong EncounterTableID { get; set; } // TableID in EncounterTable8a + [FlatBufferItem(1)] public ConditionType8a ConditionTypeID { get; set; } + [FlatBufferItem(2)] public Condition8a ConditionID { get; set; } + [FlatBufferItem(3)] public string ConditionArg1 { get; set; } = string.Empty; + [FlatBufferItem(4)] public string ConditionArg2 { get; set; } = string.Empty; + [FlatBufferItem(5)] public string ConditionArg3 { get; set; } = string.Empty; + [FlatBufferItem(6)] public string ConditionArg4 { get; set; } = string.Empty; + [FlatBufferItem(7)] public string ConditionArg5 { get; set; } = string.Empty; + [FlatBufferItem(8)] public int BonusLevelMin { get; set; } + [FlatBufferItem(9)] public int BonusLevelMax { get; set; } + + public override string ToString() => $"Spawn_20(/* EncounterTableId = */ {EncounterTable8a.GetTableName(EncounterTableID)}, /* ConditionTypeID = */ {this.GetConditionTypeSummary()}, /* Condition = */ {this.GetConditionSummary()}, {BonusLevelMin}, {BonusLevelMax})"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerF218a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerF218a.cs new file mode 100644 index 00000000..0580d132 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSpawnerF218a.cs @@ -0,0 +1,60 @@ +using System; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementSpawnerF218a +{ + [FlatBufferItem(0)] public ulong VarHash0 { get; set; } + [FlatBufferItem(1)] public ulong VarHash1 { get; set; } // Sometimes contains Slot ID from specific encounter table specified by the F20 in the same Spawner? + [FlatBufferItem(2)] public ulong VarHash2 { get; set; } // Sometimes contains Slot ID from specific encounter table specified by the F20 in the same Spawner? + [FlatBufferItem(3)] public ulong VarHash3 { get; set; } // ??? Is this as 01/02? + [FlatBufferItem(4)] public ulong VarHash4 { get; set; } // ??? Is this as 01/02? + [FlatBufferItem(5)] public ulong VarHash5 { get; set; } // ??? Is this as 01/02? + [FlatBufferItem(6)] public PlacementV3f8a Field_06 { get; set; } = new(); + [FlatBufferItem(7)] public string Field_07 { get; set; } = string.Empty; + [FlatBufferItem(8)] public float Scalar { get; set; } + [FlatBufferItem(9)] public PlacementV3f8a Field_09 { get; set; } = new(); + [FlatBufferItem(10)] public PlacementV3f8a Field_10 { get; set; } = new(); + [FlatBufferItem(11)] public int NumVarHashes { get; set; } + + public string SlotSummary + { + get + { + var rawSlots = new[] { VarHash0, VarHash1, VarHash2, VarHash3, VarHash4, VarHash5 }; + for (var i = 0; i < NumVarHashes; i++) + { + if (rawSlots[i] == 0xCBF29CE484222645) + throw new ArgumentException("VarHash shouldn't be empty!"); + } + for (var i = NumVarHashes; i < rawSlots.Length; i++) + { + if (rawSlots[i] != 0xCBF29CE484222645) + throw new ArgumentException("VarHash should be empty!"); + } + + if (NumVarHashes == 0) + return "/* Slots = */ None()"; + + if (NumVarHashes == 1) + throw new ArgumentException("Invalid NumVarHashes!"); + + var slots = rawSlots.Skip(1).Take(NumVarHashes - 1).Select(EncounterSlot8a.GetSlotName); + + return $"/* Slots = */ 0x{VarHash0:X16}({string.Join(", ", slots)})"; + } + } + + public override string ToString() => $"Spawn_21({SlotSummary}, {Field_06}, \"{Field_07}\", {Scalar}, {Field_09}, {Field_10})"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementUnnnTable.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementUnnnTable.cs new file mode 100644 index 00000000..4af64222 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementUnnnTable.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementUnnnTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlacementUnnnEntry[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementUnnnEntry +{ + [FlatBufferItem(0)] public string Identifier { get; set; } = string.Empty; + [FlatBufferItem(1)] public ulong Hash_01 { get; set; } + [FlatBufferItem(2)] public PlacementParameters8a[] Field_03 { get; set; } = Array.Empty(); + [FlatBufferItem(3)] public ulong Hash_03 { get; set; } + [FlatBufferItem(4)] public int Number { get; set; } + [FlatBufferItem(5)] public bool Flag { get; set; } + + public PlacementParameters8a Parameters + { + get { if (Field_03.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_03)}"); return Field_03[0]; } + set { if (Field_03.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_03)}"); Field_03[0] = value; } + } + + public IEnumerable GetIntersectingLocations(IReadOnlyList locations, float bias) + { + var c = Parameters.Coordinates; + return GetIntersectingLocations(locations, bias, c, 0 + bias); + } + + private static IEnumerable GetIntersectingLocations(IReadOnlyList locations, float bias, PlacementV3f8a c, float scalar) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.IntersectsSphere(c.X, c.Y, c.Z, scalar)) + result.Add(loc); + } + + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } + + public IEnumerable GetContainingLocations(IReadOnlyList locations) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.Contains(Parameters.Coordinates)) + result.Add(loc); + } + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementV3f8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementV3f8a.cs new file mode 100644 index 00000000..f7899bc9 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementV3f8a.cs @@ -0,0 +1,63 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementV3f8a : IEquatable +{ + [FlatBufferItem(0)] public float X { get; set; } + [FlatBufferItem(1)] public float Y { get; set; } + [FlatBufferItem(2)] public float Z { get; set; } + public bool IsDefault => X is 1.0f && Y is 1.0f && Z is 1.0f; + + public double DistanceTo(PlacementV3f8a other) => DistanceTo(other.X, other.Y, other.Z); + public double DistanceTo(float x, float y, float z) => Math.Sqrt(Math.Pow(X - x, 2) + Math.Pow(Y - y, 2) + Math.Pow(Z - z, 2)); + + public PlacementV3f8a Clone() => new() + { + X = X, + Y = Y, + Z = Z, + }; + + public override string ToString() => $"V3f({X}, {Y}, {Z})"; + public string ToTriple() => $"({X}, {Y}, {Z})"; + + public bool Equals(PlacementV3f8a? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z); + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((PlacementV3f8a)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = X.GetHashCode(); + hashCode = (hashCode * 397) ^ Y.GetHashCode(); + hashCode = (hashCode * 397) ^ Z.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(PlacementV3f8a? left, PlacementV3f8a? right) => Equals(left, right); + public static bool operator !=(PlacementV3f8a? left, PlacementV3f8a? right) => !Equals(left, right); +} \ No newline at end of file diff --git a/pkNX.Structures.FlatBuffers/Arceus/PlayReportTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/PlayReportTable8a.cs new file mode 100644 index 00000000..3a3e7bb5 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PlayReportTable8a.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlayReportTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlayReportItem8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlayReportItem8a +{ + [FlatBufferItem(00)] public string Field_00 { get; set; } = string.Empty; + [FlatBufferItem(01)] public ulong Field_01 { get; set; } + [FlatBufferItem(02)] public ulong Field_02 { get; set; } + [FlatBufferItem(03)] public ulong Field_03 { get; set; } + [FlatBufferItem(04)] public ulong Field_04 { get; set; } + [FlatBufferItem(05)] public ulong Field_05 { get; set; } + [FlatBufferItem(06)] public ulong Field_06 { get; set; } + [FlatBufferItem(07)] public ulong Field_07 { get; set; } + [FlatBufferItem(08)] public ulong Field_08 { get; set; } + [FlatBufferItem(09)] public ulong Field_09 { get; set; } + [FlatBufferItem(10)] public ulong Field_10 { get; set; } + [FlatBufferItem(11)] public ulong Field_11 { get; set; } + [FlatBufferItem(12)] public ulong Field_12 { get; set; } + [FlatBufferItem(13)] public ulong Field_13 { get; set; } + [FlatBufferItem(14)] public ulong Field_14 { get; set; } + [FlatBufferItem(15)] public ulong Field_15 { get; set; } + [FlatBufferItem(16)] public ulong Field_16 { get; set; } + [FlatBufferItem(17)] public ulong Field_17 { get; set; } + [FlatBufferItem(18)] public ulong Field_18 { get; set; } + [FlatBufferItem(19)] public ulong Field_19 { get; set; } + [FlatBufferItem(20)] public ulong Field_20 { get; set; } + [FlatBufferItem(21)] public ulong Field_21 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeAIArchive8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeAIArchive8a.cs new file mode 100644 index 00000000..28f7ed2c --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeAIArchive8a.cs @@ -0,0 +1,95 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAIArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokeAI8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAI8a +{ + [FlatBufferItem(00)] public int Species { get; set; } // int + [FlatBufferItem(01)] public int Form { get; set; } // int + [FlatBufferItem(02)] public bool IsAlpha { get; set; } // bool + [FlatBufferItem(03)] public string Behavior1 { get; set; } = string.Empty; + [FlatBufferItem(04)] public string Behavior2 { get; set; } = string.Empty; + [FlatBufferItem(05)] public string Behavior3 { get; set; } = string.Empty; + [FlatBufferItem(06)] public bool Field_06 { get; set; } // bool + [FlatBufferItem(07)] public ulong Field_07 { get; set; } // ulong + [FlatBufferItem(08)] public bool Field_08 { get; set; } // bool, always true + [FlatBufferItem(09)] public PokeAI8a_F09 Field_09 { get; set; } = new(); + [FlatBufferItem(10)] public bool Field_10 { get; set; } // bool, always true + [FlatBufferItem(11)] public PokeAI8a_F09 Field_11 { get; set; } = new(); + [FlatBufferItem(12)] public bool Field_12 { get; set; } // bool, always true except for Darkrai + [FlatBufferItem(13)] public PokeAI8a_F09 Field_13 { get; set; } = new(); + [FlatBufferItem(14)] public bool Field_14 { get; set; } // bool, always true + [FlatBufferItem(15)] public PokeAI8a_F09 Field_15 { get; set; } = new(); + [FlatBufferItem(16)] public bool Field_16 { get; set; } // bool, always true + [FlatBufferItem(17)] public PokeAI8a_F09 Field_17 { get; set; } = new(); + [FlatBufferItem(18)] public PokeAI8a_F18 Field_18 { get; set; } = new(); // only used by Kleavor-1 + [FlatBufferItem(19)] public PokeAI8a_F18 Field_19 { get; set; } = new(); // assumed same as above, none use + [FlatBufferItem(20)] public PokeAI8a_F18 Field_20 { get; set; } = new(); // assumed same as above, none use + [FlatBufferItem(21)] public PokeAI8a_F18 Field_21 { get; set; } = new(); // assumed same as above, only used by Kleavor-1 with fields 0,1,2 + [FlatBufferItem(22)] public string MoveEffect1 { get; set; } = string.Empty; + [FlatBufferItem(23)] public string MoveEffect2 { get; set; } = string.Empty; + [FlatBufferItem(24)] public PokeAI8a_F24 Field_24 { get; set; } = new(); + [FlatBufferItem(25)] public float Field_25 { get; set; } // float + [FlatBufferItem(26)] public float Field_26 { get; set; } // float + [FlatBufferItem(27)] public PlacementV3f8a Field_27 { get; set; } = new(); + [FlatBufferItem(28)] public PlacementV3f8a Field_28 { get; set; } = new(); + [FlatBufferItem(29)] public PlacementV3f8a Field_29 { get; set; } = new(); + [FlatBufferItem(30)] public PlacementV3f8a Field_30 { get; set; } = new(); + [FlatBufferItem(31)] public int Field_31 { get; set; } // int + [FlatBufferItem(32)] public int Field_32 { get; set; } // int +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAI8a_F24 +{ + [FlatBufferItem(00)] public float Field_00 { get; set; } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAI8a_F18 +{ + [FlatBufferItem(00)] public bool Field_00 { get; set; } + [FlatBufferItem(01)] public float Field_01 { get; set; } + [FlatBufferItem(02)] public float Field_02 { get; set; } + [FlatBufferItem(03)] public float Field_03 { get; set; } + [FlatBufferItem(04)] public float Field_04 { get; set; } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAI8a_F09 +{ + [FlatBufferItem(00)] public PokeAI8_F08 Field_00 { get; set; } = new(); + [FlatBufferItem(01)] public PokeAI8_F08 Field_01 { get; set; } = new(); + [FlatBufferItem(02)] public PokeAI8_F08 Field_02 { get; set; } = new(); + [FlatBufferItem(03)] public PokeAI8_F08 Field_03 { get; set; } = new(); + [FlatBufferItem(04)] public PokeAI8_F08 Field_04 { get; set; } = new(); + [FlatBufferItem(05)] public PokeAI8_F08 Field_05 { get; set; } = new(); + [FlatBufferItem(06)] public PokeAI8_F08 Field_06 { get; set; } = new(); + [FlatBufferItem(07)] public PokeAI8_F08 Field_07 { get; set; } = new(); + [FlatBufferItem(08)] public PokeAI8_F08 Field_08 { get; set; } = new(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAI8_F08 +{ + [FlatBufferItem(00)] public ulong Hash { get; set; } + [FlatBufferItem(01)] public string[] Field_01 { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeAdd8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeAdd8a.cs new file mode 100644 index 00000000..a9faba1d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeAdd8a.cs @@ -0,0 +1,82 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAdd8aArchive : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokeAdd8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeAdd8a +{ + [FlatBufferItem(00)] public string Field_00 { get; set; } = string.Empty; + [FlatBufferItem(01)] public int Species { get; set; } + [FlatBufferItem(02)] public int Form { get; set; } + [FlatBufferItem(03)] public int Gender { get; set; } + [FlatBufferItem(04)] public int ShinyLock { get; set; } + [FlatBufferItem(05)] public int Level { get; set; } + [FlatBufferItem(06)] public int AbilityRandType { get; set; } + [FlatBufferItem(07)] public int Nature { get; set; } + [FlatBufferItem(08)] public int Height { get; set; } + [FlatBufferItem(09)] public int Weight { get; set; } + [FlatBufferItem(10)] public bool IsOybn { get; set; } + [FlatBufferItem(11)] public int Field_11 { get; set; } // All Entries have empty + [FlatBufferItem(12)] public int Field_12 { get; set; } // All Entries have empty + [FlatBufferItem(13)] public int Field_13 { get; set; } // All Entries have empty + [FlatBufferItem(14)] public int Field_14 { get; set; } // All Entries have empty + [FlatBufferItem(15)] public int Field_15 { get; set; } // All Entries have empty + [FlatBufferItem(16)] public int Field_16 { get; set; } // All Entries have empty + [FlatBufferItem(17)] public int Field_17 { get; set; } // All Entries have empty + [FlatBufferItem(18)] public int Field_18 { get; set; } // All Entries have empty + [FlatBufferItem(19)] public int IV_HP { get; set; } + [FlatBufferItem(20)] public int IV_ATK { get; set; } + [FlatBufferItem(21)] public int IV_DEF { get; set; } + [FlatBufferItem(22)] public int IV_SPA { get; set; } + [FlatBufferItem(23)] public int IV_SPD { get; set; } + [FlatBufferItem(24)] public int IV_SPE { get; set; } + [FlatBufferItem(25)] public int NumPerfectIvs { get; set; } + [FlatBufferItem(26)] public int GV_HP { get; set; } + [FlatBufferItem(27)] public int GV_ATK { get; set; } + [FlatBufferItem(28)] public int GV_DEF { get; set; } + [FlatBufferItem(29)] public int GV_SPA { get; set; } + [FlatBufferItem(30)] public int GV_SPD { get; set; } + [FlatBufferItem(31)] public int GV_SPE { get; set; } + + public string Dump(string[] speciesNames) + { + var natureStr = Nature == -1 ? "" : $", Nature = {Nature}"; + var shinyStr = $", Shiny = {(ShinyLock == 2 ? "Never" : ShinyLock.ToString())}"; + var gvStr = ""; + if (GV_HP != 0) + gvStr += $", GV_HP = {GV_HP}"; + if (GV_ATK != 0) + gvStr += $", GV_ATK = {GV_ATK}"; + if (GV_DEF != 0) + gvStr += $", GV_DEF = {GV_DEF}"; + if (GV_SPA != 0) + gvStr += $", GV_SPA = {GV_SPA}"; + if (GV_SPD != 0) + gvStr += $", GV_SPD = {GV_SPD}"; + if (GV_SPE != 0) + gvStr += $", GV_SPE = {GV_SPE}"; + + var hw = $"HeightScalar = {Height:000}, WeightScalar = {Weight:000}"; + var genderStr = Gender == -1 ? "" : $", Gender = {Gender}"; + var iv = NumPerfectIvs == 0 ? "" : $", FlawlessIVCount = {NumPerfectIvs}"; + string comment = $"// {speciesNames[Species]}{(Form == 0 ? "" : $"-{Form}")}"; + return $"new({Species:000},{Form:000},{Level:00}) {{ Gift = true, {hw}{shinyStr}{genderStr}{natureStr}{iv}{gvStr} }}, {comment}"; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeDropItemArchive8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeDropItemArchive8a.cs new file mode 100644 index 00000000..60bae10b --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeDropItemArchive8a.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeDropItemArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokeDropItem8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeDropItem8a +{ + [FlatBufferItem(00)] public ulong Hash { get; set; } + [FlatBufferItem(01)] public int Field_01 { get; set; } + [FlatBufferItem(02)] public int Field_02 { get; set; } + [FlatBufferItem(03)] public int Field_03 { get; set; } + [FlatBufferItem(04)] public int Field_04 { get; set; } + + public string Dump(string[] itemNames) => $"{Hash:X16}\t{itemNames[Field_01]}\t{Field_02}\t{itemNames[Field_03]}\t{Field_04}"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeDropItemBattleArchive8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeDropItemBattleArchive8a.cs new file mode 100644 index 00000000..46afb1b0 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeDropItemBattleArchive8a.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeDropItemBattleArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokeDropItemBattle8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeDropItemBattle8a +{ + [FlatBufferItem(00)] public int RateItem1 { get; set; } + [FlatBufferItem(01)] public int IndexItem1 { get; set; } + [FlatBufferItem(02)] public int RateItem2 { get; set; } + [FlatBufferItem(03)] public int IndexItem2 { get; set; } + [FlatBufferItem(04)] public int RateItem3 { get; set; } + [FlatBufferItem(05)] public int IndexItem3 { get; set; } + [FlatBufferItem(06)] public int RateItem4 { get; set; } + [FlatBufferItem(07)] public int IndexItem4 { get; set; } + [FlatBufferItem(08)] public int RateItem5 { get; set; } + [FlatBufferItem(09)] public int IndexItem5 { get; set; } + + public string Dump(string[] n) => $"{RateItem1}\t{n[IndexItem1]}\t{RateItem2}\t{n[IndexItem2]}\t{RateItem3}\t{n[IndexItem3]}\t{RateItem4}\t{n[IndexItem4]}\t{RateItem5}\t{n[IndexItem5]}"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeMiscTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeMiscTable8a.cs new file mode 100644 index 00000000..f8cea0c4 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Poke/PokeMiscTable8a.cs @@ -0,0 +1,50 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeMiscTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokeMisc8a[] Table { get; set; } = Array.Empty(); + + public PokeMisc8a GetEntry(int species, int form) => + Array.Find(Table, z => z.Species == species && z.Form == z.Form) ?? + throw new IndexOutOfRangeException($"{species}-{form} is not in {nameof(PokeMiscTable8a)}."); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeMisc8a +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public int Form { get; set; } + [FlatBufferItem(02)] public int Field_02 { get; set; } + [FlatBufferItem(03)] public float Field_03 { get; set; } + [FlatBufferItem(04)] public float Field_04 { get; set; } + [FlatBufferItem(05)] public int Field_05 { get; set; } + [FlatBufferItem(06)] public int OybnLevelIndex { get; set; } + [FlatBufferItem(07)] public ulong Hash_07 { get; set; } + [FlatBufferItem(08)] public ulong Hash_08 { get; set; } + [FlatBufferItem(09)] public string Value { get; set; } = string.Empty; + [FlatBufferItem(10)] public int[] Field_10 { get; set; } = Array.Empty(); + [FlatBufferItem(11)] public int Field_11 { get; set; } + [FlatBufferItem(12)] public int Field_12 { get; set; } + [FlatBufferItem(13)] public int Field_13 { get; set; } + [FlatBufferItem(14)] public bool Field_14 { get; set; } + [FlatBufferItem(15)] public int Field_15 { get; set; } + [FlatBufferItem(16)] public float Field_16 { get; set; } + [FlatBufferItem(17)] public float Field_17 { get; set; } + [FlatBufferItem(18)] public bool Field_18 { get; set; } + [FlatBufferItem(19)] public float Field_19 { get; set; } + [FlatBufferItem(20)] public float Field_20 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokeResource/PokeInfoList.cs b/pkNX.Structures.FlatBuffers/Arceus/PokeResource/PokeInfoList.cs new file mode 100644 index 00000000..df3dae7c --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PokeResource/PokeInfoList.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfoList8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public int TableDataLength { get; set; } + [FlatBufferItem(1)] public PokeInfo8a[] Table { get; set; } = Array.Empty(); + + public (ushort[] SF, byte[] Gender) Parse() + { + var entries = GetDexFormEntries().GroupBy(z => (int)(ushort)z) + .Select(Merge).Distinct().OrderBy(z => (ushort)z).ToList(); + var SF = new ushort[entries.Count]; + var Gender = new byte[entries.Count]; + for (int i = 0; i < SF.Length; i++) + { + SF[i] = (ushort)entries[i]; + Gender[i] = (byte)(entries[i] >> 16); + } + return (SF, Gender); + } + + private static int Merge(IGrouping arg) + { + var species = arg.Key; + foreach (var value in arg) + species |= value; + return species; + } + + public IEnumerable GetDexFormEntries() + { + foreach (var x in Table) + { + var s = x.Species; + foreach (var fe in x.Children) + { + var f = fe.Form; + foreach (var w in fe.Detail) + { + bool rare = w.IsRare; + foreach (var z in w.Detail) + { + var unk = z.Unknown; + foreach (var r in z.Detail) + { + var gender = r.Gender.GenderValue; + yield return (s | (f << 11)) | ((1 << (int)gender) << 16); + } + } + } + } + } + } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfo8a +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public PokeInfoDetail8a[] Children { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfoDetail8a +{ + [FlatBufferItem(00)] public int Form { get; set; } + [FlatBufferItem(01)] public PokeInfoDetail8a_2[] Detail { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfoDetail8a_2 +{ + [FlatBufferItem(00)] public bool IsRare { get; set; } + [FlatBufferItem(01)] public PokeInfoDetail8a_3[] Detail { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfoDetail8a_3 +{ + [FlatBufferItem(00)] public bool Unknown { get; set; } + [FlatBufferItem(01)] public PokeInfoDetail8a_5[] Detail { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfoDetail8a_5 +{ + [FlatBufferItem(00)] public PokeInfoGender8a Gender { get; set; } = new(); + [FlatBufferItem(01)] public string AssetName { get; set; } = string.Empty; +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeInfoGender8a +{ + [FlatBufferItem(00)] public PokeInfoGender GenderValue { get; set; } +} + +[FlatBufferEnum(typeof(byte))] +public enum PokeInfoGender : byte +{ + GenderDiffMale, + GenderDiffFemale, + Genderless, + DualGenderNoDifference, + MaleOnly, + FemaleOnly, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokeResource/PokeResourceTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/PokeResource/PokeResourceTable8a.cs new file mode 100644 index 00000000..5c7c6875 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PokeResource/PokeResourceTable8a.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +// poke_resource_table.trpmcatalog +/// for Legends: Arceus, adding +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeResourceTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(00)] public PokeResourceMeta8a Meta { get; set; } = new(); + [FlatBufferItem(01)] public PokeModelConfig8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeResourceMeta8a +{ + [FlatBufferItem(00)] public int Field0 { get; set; } = 5; // 4 in prior game format + [FlatBufferItem(01)] public int Field1 { get; set; } = 4; // 2 in prior game format +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeModelConfig8a +{ + [FlatBufferItem(00)] public PokeModelMeta8a Meta { get; set; } = new(); + [FlatBufferItem(01)] public string PathModel { get; set; } = string.Empty; // string + [FlatBufferItem(02)] public string PathConfig { get; set; } = string.Empty; // string + [FlatBufferItem(03)] public string PathArchive { get; set; } = string.Empty; // string + [FlatBufferItem(04)] public byte Unused { get; set; } // unused! + [FlatBufferItem(05)] public AnimationConfigStringTuple8a[] Field_05 { get; set; } = Array.Empty(); + [FlatBufferItem(06)] public AnimationConfigStringTuple8a[] Field_06 { get; set; } = Array.Empty(); + [FlatBufferItem(07)] public int? ArceusType { get; set; } // Specified by Arceus' forms -- NEW! +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeModelMeta8a +{ + [FlatBufferItem(00)] public ushort Species { get; set; } + [FlatBufferItem(01)] public ushort Form { get; set; } + [FlatBufferItem(02)] public byte Gender { get; set; } + [FlatBufferItem(03)] public byte Shiny { get; set; } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AnimationConfigStringTuple8a +{ + [FlatBufferItem(00)] public string Name { get; set; } = string.Empty; + [FlatBufferItem(01)] public string Path { get; set; } = string.Empty; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokedexDistributionTable.cs b/pkNX.Structures.FlatBuffers/Arceus/PokedexDistributionTable.cs new file mode 100644 index 00000000..9e6b51a5 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PokedexDistributionTable.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokedexDistributionTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokedexDistributionEntry[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokedexDistributionEntry +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public int Field_01 { get; set; } + [FlatBufferItem(02)] public int Field_02 { get; set; } + [FlatBufferItem(03)] public int Field_03 { get; set; } + [FlatBufferItem(04)] public int Field_04 { get; set; } + [FlatBufferItem(05)] public int Field_05 { get; set; } + [FlatBufferItem(06)] public int Field_06 { get; set; } + [FlatBufferItem(07)] public int Field_07 { get; set; } + [FlatBufferItem(08)] public int Field_08 { get; set; } + [FlatBufferItem(09)] public int Field_09 { get; set; } + [FlatBufferItem(10)] public int Field_10 { get; set; } + [FlatBufferItem(11)] public int Field_11 { get; set; } + [FlatBufferItem(12)] public int Field_12 { get; set; } + [FlatBufferItem(13)] public ulong Field_13 { get; set; } + [FlatBufferItem(14)] public ulong Field_14 { get; set; } + [FlatBufferItem(15)] public ulong Field_15 { get; set; } + [FlatBufferItem(16)] public ulong Field_16 { get; set; } + [FlatBufferItem(17)] public ulong Field_17 { get; set; } + [FlatBufferItem(18)] public ulong Field_18 { get; set; } + [FlatBufferItem(19)] public ulong Field_19 { get; set; } + [FlatBufferItem(20)] public ulong Field_20 { get; set; } + [FlatBufferItem(21)] public ulong Field_21 { get; set; } + [FlatBufferItem(22)] public ulong Field_22 { get; set; } + [FlatBufferItem(23)] public ulong Field_23 { get; set; } + [FlatBufferItem(24)] public ulong Field_24 { get; set; } + [FlatBufferItem(25)] public ulong Field_25 { get; set; } + [FlatBufferItem(26)] public ulong Field_26 { get; set; } + [FlatBufferItem(27)] public ulong Field_27 { get; set; } + [FlatBufferItem(28)] public ulong Field_28 { get; set; } + [FlatBufferItem(29)] public ulong Field_29 { get; set; } + [FlatBufferItem(30)] public ulong Field_30 { get; set; } + [FlatBufferItem(31)] public ulong Field_31 { get; set; } + [FlatBufferItem(32)] public ulong Field_32 { get; set; } + [FlatBufferItem(33)] public ulong Field_33 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokedexRankTable.cs b/pkNX.Structures.FlatBuffers/Arceus/PokedexRankTable.cs new file mode 100644 index 00000000..a724f465 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PokedexRankTable.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokedexRankTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokedexRankLevel[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokedexRankLevel +{ + [FlatBufferItem(0)] public int Rank { get; set; } + [FlatBufferItem(1)] public int Required { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokedexResearchTable.cs b/pkNX.Structures.FlatBuffers/Arceus/PokedexResearchTable.cs new file mode 100644 index 00000000..529abe6e --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PokedexResearchTable.cs @@ -0,0 +1,42 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokedexResearchTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokedexResearchTask[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokedexResearchTask +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public int Task { get; set; } + [FlatBufferItem(02)] public int Threshold { get; set; } + [FlatBufferItem(03)] public int Move { get; set; } + [FlatBufferItem(04)] public int Type { get; set; } + [FlatBufferItem(05)] public int TimeOfDay { get; set; } + [FlatBufferItem(06)] public ulong Hash_06 { get; set; } + [FlatBufferItem(07)] public ulong Hash_07 { get; set; } + [FlatBufferItem(08)] public ulong Hash_08 { get; set; } + [FlatBufferItem(09)] public int Threshold1 { get; set; } + [FlatBufferItem(10)] public int Threshold2 { get; set; } + [FlatBufferItem(11)] public int Threshold3 { get; set; } + [FlatBufferItem(12)] public int Threshold4 { get; set; } + [FlatBufferItem(13)] public int Threshold5 { get; set; } + [FlatBufferItem(14)] public int PointsSingle { get; set; } + [FlatBufferItem(15)] public int PointsBonus { get; set; } + [FlatBufferItem(16)] public bool RequiredForCompletion { get; set; } // Unused but referenced by code (bool is set to != 0) +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs b/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs new file mode 100644 index 00000000..b63f8d41 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokemonRare8aTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PokemonRare8aEntry[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokemonRare8aEntry +{ + [FlatBufferItem(00)] public string Name { get; set; } = string.Empty; + [FlatBufferItem(01)] public ulong Hash { get; set; } + [FlatBufferItem(02)] public byte UnusedValue { get; set; } // none have this + [FlatBufferItem(03)] public string Option { get; set; } = string.Empty; + [FlatBufferItem(04)] public string Value { get; set; } = string.Empty; + [FlatBufferItem(05)] public string[] Field_05 { get; set; } = Array.Empty(); + [FlatBufferItem(06)] public FlatDummyEntry[] UnusedArray { get; set; } = Array.Empty(); // none have this +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Resident/AreaSettingsTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Resident/AreaSettingsTable8a.cs new file mode 100644 index 00000000..2f6ee845 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Resident/AreaSettingsTable8a.cs @@ -0,0 +1,109 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AreaSettingsTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public AreaSettings8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AreaSettings8a +{ + [FlatBufferItem(00)] public string Name { get; set; } = string.Empty; + [FlatBufferItem(01)] public string NameParent { get; set; } = string.Empty; + [FlatBufferItem(02)] public PlacementV3f8a Position { get; set; } = new(); + [FlatBufferItem(03)] public bool Flag_03 { get; set; } + [FlatBufferItem(04)] public bool Flag_04 { get; set; } + [FlatBufferItem(05)] public bool Flag_05 { get; set; } + [FlatBufferItem(06)] public bool Flag_06 { get; set; } + [FlatBufferItem(07)] public bool Flag_07 { get; set; } + [FlatBufferItem(08)] public bool Flag_08 { get; set; } + [FlatBufferItem(09)] public string NPC { get; set; } = string.Empty; + [FlatBufferItem(10)] public string NPCPokemon { get; set; } = string.Empty; + [FlatBufferItem(11)] public string BgParts { get; set; } = string.Empty; + [FlatBufferItem(12)] public string Emitter { get; set; } = string.Empty; + [FlatBufferItem(13)] public string Item { get; set; } = string.Empty; + [FlatBufferItem(14)] public string SearchItem { get; set; } = string.Empty; + [FlatBufferItem(15)] public string Spawners { get; set; } = string.Empty; + [FlatBufferItem(16)] public string Pos { get; set; } = string.Empty; + [FlatBufferItem(17)] public string BgEvent { get; set; } = string.Empty; + [FlatBufferItem(18)] public string Encounters { get; set; } = string.Empty; + [FlatBufferItem(19)] public string NavMesh { get; set; } = string.Empty; + [FlatBufferItem(20)] public string TerrainTable { get; set; } = string.Empty; + [FlatBufferItem(21)] public string NameOther { get; set; } = string.Empty; + [FlatBufferItem(22)] public bool Flag_22 { get; set; } + [FlatBufferItem(23)] public bool Flag_23 { get; set; } + [FlatBufferItem(24)] public string LandmarkItems { get; set; } = string.Empty; + [FlatBufferItem(25)] public string LandmarkItemSpawns { get; set; } = string.Empty; + [FlatBufferItem(26)] public string AttributeInfo { get; set; } = string.Empty; + [FlatBufferItem(27)] public string Locations { get; set; } = string.Empty; + [FlatBufferItem(28)] public string BattleStartPoint { get; set; } = string.Empty; + [FlatBufferItem(29)] public string SoundEvent { get; set; } = string.Empty; + [FlatBufferItem(30)] public string AreaCamera { get; set; } = string.Empty; + [FlatBufferItem(31)] public string TargetAI { get; set; } = string.Empty; + [FlatBufferItem(32)] public string Slot0 { get; set; } = string.Empty; + [FlatBufferItem(33)] public AreaSettings8a_F50 Field_33 { get; set; } = new(); // not right? + [FlatBufferItem(34)] public string Wormholes { get; set; } = string.Empty; + [FlatBufferItem(35)] public string WormholeSpawners { get; set; } = string.Empty; + [FlatBufferItem(36)] public string WormholeItems { get; set; } = string.Empty; + [FlatBufferItem(37)] public string OverViewDepth { get; set; } = string.Empty; + [FlatBufferItem(38)] public string RealTimeEventData { get; set; } = string.Empty; + [FlatBufferItem(39)] public string Mkrg { get; set; } = string.Empty; + [FlatBufferItem(40)] public string AreaWall { get; set; } = string.Empty; + [FlatBufferItem(41)] public string Balloon { get; set; } = string.Empty; + [FlatBufferItem(42)] public string UnownSpawners { get; set; } = string.Empty; + [FlatBufferItem(43)] public string MultiShapeSoundEvent { get; set; } = string.Empty; + [FlatBufferItem(44)] public ulong Field_44 { get; set; } + [FlatBufferItem(45)] public string Path { get; set; } = string.Empty; + [FlatBufferItem(46)] public string PopupEvent { get; set; } = string.Empty; + [FlatBufferItem(47)] public string Invisible { get; set; } = string.Empty; + [FlatBufferItem(48)] public string LocalWeather { get; set; } = string.Empty; + [FlatBufferItem(49)] public string SoundBank { get; set; } = string.Empty; + [FlatBufferItem(50)] public AreaSettings8a_F50 Field_50 { get; set; } = new(); + [FlatBufferItem(51)] public string Archive { get; set; } = string.Empty; + [FlatBufferItem(52)] public AreaSettings8a_F52 Field_52 { get; set; } = new(); + [FlatBufferItem(53)] public PlacementV3f8a Field_53 { get; set; } = new(); + [FlatBufferItem(54)] public ulong Hash { get; set; } + [FlatBufferItem(55)] public bool Field_55 { get; set; } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AreaSettings8a_F52 +{ +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AreaSettings8a_F50 +{ +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AreaSettings8a_F33 +{ + [FlatBufferItem(00)] public ulong Field_00 { get; set; } + [FlatBufferItem(01)] public int[] Field_01 { get; set; } = Array.Empty(); // unknown + [FlatBufferItem(02)] public AreaSettings8a_F33a Field_02 { get; set; } = new(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AreaSettings8a_F33a +{ + [FlatBufferItem(00)] public byte Field_00 { get; set; } // unknown + [FlatBufferItem(01)] public byte Field_01 { get; set; } // unknown + [FlatBufferItem(02)] public int[] Field_02 { get; set; } = Array.Empty(); // unknown + [FlatBufferItem(03)] public int[] Field_03 { get; set; } = Array.Empty(); // unknown + [FlatBufferItem(04)] public ulong Field_04 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/ScriptIDRecordRelease.cs b/pkNX.Structures.FlatBuffers/Arceus/ScriptIDRecordRelease.cs new file mode 100644 index 00000000..012e894d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/ScriptIDRecordRelease.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ScriptIDRecordRelease : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public ScriptIDRecord[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ScriptIDRecord +{ + [FlatBufferItem(00)] public string Name { get; set; } = string.Empty; + [FlatBufferItem(01)] public string Type { get; set; } = string.Empty; + [FlatBufferItem(02)] public string Lua { get; set; } = string.Empty; + [FlatBufferItem(03)] public string Dat { get; set; } = string.Empty; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterData8Archive.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterData8Archive.cs new file mode 100644 index 00000000..9b96e9d5 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterData8Archive.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterDataArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public EncounterTable8a[] Table { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterEligiblityTraits8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterEligiblityTraits8a.cs new file mode 100644 index 00000000..89f36c6d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterEligiblityTraits8a.cs @@ -0,0 +1,62 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterEligiblityTraits8a : IHasCondition8a +{ + [FlatBufferItem(0)] public ConditionType8a ConditionTypeID { get; set; } + [FlatBufferItem(1)] public Condition8a ConditionID { get; set; } + [FlatBufferItem(2)] public string ConditionArg1 { get; set; } = string.Empty; + [FlatBufferItem(3)] public string ConditionArg2 { get; set; } = string.Empty; + [FlatBufferItem(4)] public string ConditionArg3 { get; set; } = string.Empty; + [FlatBufferItem(5)] public string ConditionArg4 { get; set; } = string.Empty; + [FlatBufferItem(6)] public string ConditionArg5 { get; set; } = string.Empty; + [FlatBufferItem(07)] public float TimeOfDayMultiplier_0 { get; set; } + [FlatBufferItem(08)] public float TimeOfDayMultiplier_1 { get; set; } + [FlatBufferItem(09)] public float TimeOfDayMultiplier_2 { get; set; } + [FlatBufferItem(10)] public float TimeOfDayMultiplier_3 { get; set; } + [FlatBufferItem(11)] public float WeatherMultiplier_1 { get; set; } + [FlatBufferItem(12)] public float WeatherMultiplier_2 { get; set; } + [FlatBufferItem(13)] public float WeatherMultiplier_3 { get; set; } + [FlatBufferItem(14)] public float WeatherMultiplier_4 { get; set; } + [FlatBufferItem(15)] public float WeatherMultiplier_5 { get; set; } + [FlatBufferItem(16)] public float WeatherMultiplier_6 { get; set; } + [FlatBufferItem(17)] public float WeatherMultiplier_7 { get; set; } + [FlatBufferItem(18)] public float WeatherMultiplier_8 { get; set; } + + public float GetTimeMultiplier(int index) => index switch + { + 0 => TimeOfDayMultiplier_0, + 1 => TimeOfDayMultiplier_1, + 2 => TimeOfDayMultiplier_2, + 3 => TimeOfDayMultiplier_3, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + + public float GetWeatherMultiplier(int index) => index switch + { + 0 => 1.0f, // "No Weather" results in no modification of rate for all species/forms + 1 => WeatherMultiplier_1, + 2 => WeatherMultiplier_2, + 3 => WeatherMultiplier_3, + 4 => WeatherMultiplier_4, + 5 => WeatherMultiplier_5, + 6 => WeatherMultiplier_6, + 7 => WeatherMultiplier_7, + 8 => WeatherMultiplier_8, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + + public bool HasTimeModifier(int index) => GetTimeMultiplier(index) != -1.0f; + public bool HasWeatherModifier(int index) => index != 0 && GetWeatherMultiplier(index) != -1.0f; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterMultiplerArchive8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterMultiplerArchive8a.cs new file mode 100644 index 00000000..055acc7e --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterMultiplerArchive8a.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterMultiplerArchive8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public EncounterMultiplier8a[] Table { get; set; } = Array.Empty(); + + public EncounterMultiplier8a GetEncounterMultiplier(EncounterSlot8a slot) + { + var result = Array.Find(Table, z => z.Species == slot.Species && z.Form == slot.Form); + if (result == null) + throw new ArgumentException($"Invalid Encounter Slot {slot.Species} - {slot.Form}"); + return result; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterMultiplier8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterMultiplier8a.cs new file mode 100644 index 00000000..501262e7 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterMultiplier8a.cs @@ -0,0 +1,57 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterMultiplier8a +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public int Form { get; set; } + [FlatBufferItem(02)] public float TimeOfDayMultiplier_0 { get; set; } + [FlatBufferItem(03)] public float TimeOfDayMultiplier_1 { get; set; } + [FlatBufferItem(04)] public float TimeOfDayMultiplier_2 { get; set; } + [FlatBufferItem(05)] public float TimeOfDayMultiplier_3 { get; set; } + [FlatBufferItem(06)] public float WeatherMultiplier_1 { get; set; } + [FlatBufferItem(07)] public float WeatherMultiplier_2 { get; set; } + [FlatBufferItem(08)] public float WeatherMultiplier_3 { get; set; } + [FlatBufferItem(09)] public float WeatherMultiplier_4 { get; set; } + [FlatBufferItem(10)] public float WeatherMultiplier_5 { get; set; } + [FlatBufferItem(11)] public float WeatherMultiplier_6 { get; set; } + [FlatBufferItem(12)] public float WeatherMultiplier_7 { get; set; } + [FlatBufferItem(13)] public float WeatherMultiplier_8 { get; set; } + + public float GetTimeMultiplier(int index) => index switch + { + 0 => TimeOfDayMultiplier_0, + 1 => TimeOfDayMultiplier_1, + 2 => TimeOfDayMultiplier_2, + 3 => TimeOfDayMultiplier_3, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + + public float GetWeatherMultiplier(int index) => index switch + { + 0 => 1.0f, // "No Weather" results in no modification of rate for all species/forms + 1 => WeatherMultiplier_1, + 2 => WeatherMultiplier_2, + 3 => WeatherMultiplier_3, + 4 => WeatherMultiplier_4, + 5 => WeatherMultiplier_5, + 6 => WeatherMultiplier_6, + 7 => WeatherMultiplier_7, + 8 => WeatherMultiplier_8, + _ => throw new ArgumentOutOfRangeException(nameof(index)), + }; + + public bool HasTimeModifier(int index) => GetTimeMultiplier(index) != -1.0f; + public bool HasWeatherModifier(int index) => index != 0 && GetWeatherMultiplier(index) != -1.0f; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterOybnTraits8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterOybnTraits8a.cs new file mode 100644 index 00000000..73a3fd3c --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterOybnTraits8a.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterOybnTraits8a +{ + [FlatBufferItem(00)] public bool Oybn1 { get; set; } + [FlatBufferItem(01)] public bool Oybn2 { get; set; } + [FlatBufferItem(02)] public bool Field_02 { get; set; } + [FlatBufferItem(03)] public bool Field_03 { get; set; } + + public bool IsOybnAny => Oybn1 || Oybn2 || Field_02 || Field_03; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterSlot8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterSlot8a.cs new file mode 100644 index 00000000..88e872cc --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterSlot8a.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.ComponentModel; +using FlatSharp.Attributes; +using pkNX.Containers; + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterSlot8a +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public ulong SlotID { get; set; } + [FlatBufferItem(02)] public int Gender { get; set; } + [FlatBufferItem(03)] public int Form { get; set; } + [FlatBufferItem(04)] public ShinyType8a ShinyLock { get; set; } + [FlatBufferItem(05)] public AbilityType8a AbilityRandType { get; set; } + [FlatBufferItem(06)] public NatureType8a Nature { get; set; } + [FlatBufferItem(07)] public int Height { get; set; } + [FlatBufferItem(08)] public int Weight { get; set; } + [FlatBufferItem(09)] public bool Field_09 { get; set; } + [FlatBufferItem(10)] public bool Field_10 { get; set; } + [FlatBufferItem(11)] public bool Field_11 { get; set; } + [FlatBufferItem(12)] public bool Field_12 { get; set; } + [FlatBufferItem(13)] public int GV_HP { get; set; } + [FlatBufferItem(14)] public int GV_ATK { get; set; } + [FlatBufferItem(15)] public int GV_DEF { get; set; } + [FlatBufferItem(16)] public int GV_SPA { get; set; } + [FlatBufferItem(17)] public int GV_SPD { get; set; } + [FlatBufferItem(18)] public int GV_SPE { get; set; } + [FlatBufferItem(19)] public int IV_HP { get; set; } + [FlatBufferItem(20)] public int IV_ATK { get; set; } + [FlatBufferItem(21)] public int IV_DEF { get; set; } + [FlatBufferItem(22)] public int IV_SPA { get; set; } + [FlatBufferItem(23)] public int IV_SPD { get; set; } + [FlatBufferItem(24)] public int IV_SPE { get; set; } + [FlatBufferItem(25)] public int NumPerfectIvs { get; set; } + [FlatBufferItem(26)] public string Behavior1 { get; set; } = string.Empty; + [FlatBufferItem(27)] public string Behavior2 { get; set; } = string.Empty; + [FlatBufferItem(28)] public byte Field_28_AffectsLottery { get; set; } + [FlatBufferItem(29)] public int BaseProbability { get; set; } + [FlatBufferItem(30)] public int OverrideMinLevel { get; set; } + [FlatBufferItem(31)] public int OverrideMaxLevel { get; set; } + [FlatBufferItem(32)] public int Field_32 { get; set; } + [FlatBufferItem(33)] public bool HasMoveset { get; set; } + [FlatBufferItem(34)] public int Move1 { get; set; } + [FlatBufferItem(35)] public int Move2 { get; set; } + [FlatBufferItem(36)] public int Move3 { get; set; } + [FlatBufferItem(37)] public int Move4 { get; set; } + [FlatBufferItem(38)] public bool Move1Mastered { get; set; } + [FlatBufferItem(39)] public bool Move2Mastered { get; set; } + [FlatBufferItem(40)] public bool Move3Mastered { get; set; } + [FlatBufferItem(41)] public bool Move4Mastered { get; set; } + [FlatBufferItem(42)] public string Unused { get; set; } = string.Empty; + [FlatBufferItem(43)] public int Field_43_Func_1A25908 { get; set; } + [FlatBufferItem(44)] public bool Field_44_SetsPropTo100Not8000 { get; set; } + [FlatBufferItem(45)] public EncounterEligiblityTraits8a Eligibility { get; set; } = new(); + [FlatBufferItem(46)] public EncounterOybnTraits8a Oybn { get; set; } = new (); + + public (bool forced, int min, int max) GetLevels(int min, int max) + { + bool force = false; + if (OverrideMinLevel is > 0 and <= 100) + { + force = true; + min = OverrideMinLevel; + } + if (OverrideMaxLevel is > 0 and <= 100) + { + force = true; + max = OverrideMaxLevel; + } + return (force, min, max); + } + + public float GetTimeModifier(int time, EncounterMultiplier8a default_mults) + { + if (Eligibility.HasTimeModifier(time)) + return Eligibility.GetTimeMultiplier(time); + if (default_mults.HasTimeModifier(time)) + return default_mults.GetTimeMultiplier(time); + return 1.0f; + } + public float GetWeatherModifier(int weather, EncounterMultiplier8a default_mults) + { + if (Eligibility.HasWeatherModifier(weather)) + return Eligibility.GetWeatherMultiplier(weather); + if (default_mults.HasWeatherModifier(weather)) + return default_mults.GetWeatherMultiplier(weather); + return 1.0f; + } + + // lazy init + private static Dictionary? _slotNameMap; + private static IReadOnlyDictionary GetSlotNameMap() => _slotNameMap ??= GenerateTableMap(); + + private static Dictionary GenerateTableMap() + { + var result = new Dictionary(); + for (var i = 0; i < 1000; i++) + { + var life = $"life{i:000}"; + var pm = $"pm{i:0000}"; + result[FnvHash.HashFnv1a_64(life)] = life; + result[FnvHash.HashFnv1a_64(pm)] = pm; + } + for (var i = 0; i < 100; i++) + { + var poke = $"poke{i:00}"; + result[FnvHash.HashFnv1a_64(poke)] = poke; + } + return result; + } + + public static string GetSlotName(ulong slotId) + { + var map = GetSlotNameMap(); + if (map.TryGetValue(slotId, out var name)) + return $"\"{name}\""; + return $"0x{slotId:X16}"; + } + + public string SlotName => GetSlotName(SlotID); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterTable8a.cs new file mode 100644 index 00000000..d944f1d4 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Slots/EncounterTable8a.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using FlatSharp.Attributes; +using pkNX.Containers; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EncounterTable8a +{ + [FlatBufferItem(0)] public ulong TableID { get; set; } + [FlatBufferItem(1)] public int MinLevel { get; set; } + [FlatBufferItem(2)] public int MaxLevel { get; set; } + [FlatBufferItem(3)] public EncounterSlot8a[] Table { get; set; } = Array.Empty(); + + // lazy init + private static Dictionary? _tableNameMap; + private static IReadOnlyDictionary GetTableNameMap() => _tableNameMap ??= GenerateTableMap(); + + private static Dictionary GenerateTableMap() + { + var result = new Dictionary(); + + result[0xCBF29CE484222645] = ""; + + var prefixes = new[] { "eve", "fly", "gmk", "lnd", "mas", "oyb", "swm", "whl" }; + var kinds = new[] { "ex", "no", "ra" }; + foreach (var prefix in prefixes) + { + foreach (var kind in kinds) + { + for (var i = 0; i < 150; i++) + { + var name = $"{prefix}_{kind}_{i:00}"; + var hash = FnvHash.HashFnv1a_64(name); + result[hash] = name; + } + } + } + + for (var area = 0; area < 6; area++) + { + for (var i = 0; i < 10; i++) + { + result[FnvHash.HashFnv1a_64($"sky_area{area}_{i:00}")] = $"sky_area{area}_{i:00}"; + } + } + + result[FnvHash.HashFnv1a_64("eve_ex_16_b")] = "eve_ex_16_b"; + result[FnvHash.HashFnv1a_64("eve_ex_17_b")] = "eve_ex_17_b"; + result[FnvHash.HashFnv1a_64("eve_ex_18_b")] = "eve_ex_18_b"; + + var gimmicks = new[] { "no", "tree", "rock", "crystal", "snow", "box" }; + foreach (var gimmick in gimmicks) + { + for (var i = 0; i < 100; i++) + { + result[FnvHash.HashFnv1a_64($"gmk_{gimmick}_{i:00}")] = $"gmk_{gimmick}_{i:00}"; + for (var j = 0; j < 3; j++) + { + result[FnvHash.HashFnv1a_64($"gmk_{gimmick}_{i:00}_{j:00}")] = $"gmk_{gimmick}_{i:00}_{j:00}"; + for (var k = 0; k < 3; k++) + { + result[FnvHash.HashFnv1a_64($"gmk_{gimmick}_{i:00}{j:00}_{k:00}")] = $"gmk_{gimmick}_{i:00}{j:00}_{k:00}"; + } + } + } + } + + return result; + } + + public static string GetTableName(ulong tableId) + { + var map = GetTableNameMap(); + if (map.TryGetValue(tableId, out var name)) + return $"\"{name}\""; + return $"0x{tableId:X16}"; + } + + public string TableName => GetTableName(TableID); + + public override string ToString() => $"{TableName} (Lv. {MinLevel}-{MaxLevel})"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncount8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncount8a.cs new file mode 100644 index 00000000..cdf11487 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncount8a.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EventEncount8a +{ + [FlatBufferItem(0)] public string EncounterName { get; set; } = string.Empty; + [FlatBufferItem(1)] public string Field_01 { get; set; } = string.Empty; + [FlatBufferItem(2)] public int Field_02 { get; set; } // All Entries have empty + [FlatBufferItem(3)] public EventEncountPoke8a[]? Table { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncount8aArchive.cs b/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncount8aArchive.cs new file mode 100644 index 00000000..2454cb87 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncount8aArchive.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EventEncount8aArchive : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public EventEncount8a[] Table { get; set; } = Array.Empty(); +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncountPoke8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncountPoke8a.cs new file mode 100644 index 00000000..acb00f01 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Static/EventEncountPoke8a.cs @@ -0,0 +1,75 @@ +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class EventEncountPoke8a +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public int Form { get; set; } + [FlatBufferItem(02)] public int Gender { get; set; } + [FlatBufferItem(03)] public int ShinyLock { get; set; } + [FlatBufferItem(04)] public int Level { get; set; } + [FlatBufferItem(05)] public int AbilityRandType { get; set; } + [FlatBufferItem(06)] public int Nature { get; set; } + [FlatBufferItem(07)] public int Height { get; set; } + [FlatBufferItem(08)] public int Weight { get; set; } + [FlatBufferItem(09)] public bool Field_09 { get; set; } + [FlatBufferItem(10)] public bool Field_10 { get; set; } + [FlatBufferItem(11)] public bool Field_11 { get; set; } + [FlatBufferItem(12)] public int Field_12 { get; set; } + [FlatBufferItem(13)] public string Field_13 { get; set; } = string.Empty; + [FlatBufferItem(14)] public string Field_14 { get; set; } = string.Empty; + [FlatBufferItem(15)] public string Field_15 { get; set; } = string.Empty; + [FlatBufferItem(16)] public int Field_16 { get; set; } + [FlatBufferItem(17)] public bool IsOybn { get; set; } + [FlatBufferItem(18)] public int Move1 { get; set; } + [FlatBufferItem(19)] public bool Mastered1 { get; set; } + [FlatBufferItem(20)] public int Move2 { get; set; } + [FlatBufferItem(21)] public bool Mastered2 { get; set; } + [FlatBufferItem(22)] public int Move3 { get; set; } + [FlatBufferItem(23)] public bool Mastered3 { get; set; } + [FlatBufferItem(24)] public int Move4 { get; set; } + [FlatBufferItem(25)] public bool Mastered4 { get; set; } + [FlatBufferItem(26)] public int IV_HP { get; set; } + [FlatBufferItem(27)] public int IV_ATK { get; set; } + [FlatBufferItem(28)] public int IV_DEF { get; set; } + [FlatBufferItem(29)] public int IV_SPA { get; set; } + [FlatBufferItem(30)] public int IV_SPD { get; set; } + [FlatBufferItem(31)] public int IV_SPE { get; set; } + [FlatBufferItem(32)] public int NumPerfectIvs { get; set; } + [FlatBufferItem(33)] public int GV_HP { get; set; } + [FlatBufferItem(34)] public int GV_ATK { get; set; } + [FlatBufferItem(35)] public int GV_DEF { get; set; } + [FlatBufferItem(36)] public int GV_SPA { get; set; } + [FlatBufferItem(37)] public int GV_SPD { get; set; } + [FlatBufferItem(38)] public int GV_SPE { get; set; } + + public bool HasMoveset => Move1 != 0; + public bool HasGVs => GV_HP != 0 || GV_ATK != 0 || GV_DEF != 0 || GV_SPA != 0 || GV_SPD != 0 || GV_SPE != 0; + + public string Dump(string[] speciesNames, string argEncounterName) + { + var natureStr = Nature == -1 ? "" : $", Nature = (int){(NatureType8a)Nature}"; + var shinyStr = $", Shiny = {(ShinyLock == 2 ? "Never" : ShinyLock.ToString())}"; + var gvStr = ""; + if (HasGVs) + gvStr = $", GVs = new[]{{{GV_HP},{GV_ATK},{GV_DEF},{GV_SPE},{GV_SPA},{GV_SPD}}}"; + + var moves = !HasMoveset ? "" : $", Moves = new[] {{{Move1:000},{Move2:000},{Move3:000},{Move4:000}}}"; + var mastery = !HasMoveset ? "" : $", Mastery = new[] {{{Mastered1.ToString().ToLower()},{Mastered2.ToString().ToLower()},{Mastered3.ToString().ToLower()},{Mastered4.ToString().ToLower()}}}"; + var alpha = !IsOybn ? "" : ", IsAlpha = true"; + var genderStr = Gender == -1 ? "" : $", Gender = {Gender}"; + var iv = NumPerfectIvs == 0 ? "" : $", FlawlessIVCount = {NumPerfectIvs}"; + string comment = $"// {argEncounterName}: {speciesNames[Species]}{(Form == 0 ? "" : $"-{Form}")}"; + return $"new({Species:000},{Form:000},{Level:00},{Height},{Weight}) {{ Location = -01{shinyStr}{alpha}{genderStr}{natureStr}{iv}{gvStr}{moves}{mastery} }}, {comment}"; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Throw/ThrowParamTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Throw/ThrowParamTable8a.cs new file mode 100644 index 00000000..dd2fc28f --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Throw/ThrowParamTable8a.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ThrowParamTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public ThrowParam8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ThrowParam8a +{ + [FlatBufferItem(00)] public ulong Hash { get; set; } + [FlatBufferItem(01)] public float Field_01 { get; set; } + [FlatBufferItem(02)] public float Field_02 { get; set; } + [FlatBufferItem(03)] public float Field_03 { get; set; } + [FlatBufferItem(04)] public float Field_04 { get; set; } + + public string Dump() => $"{Hash:X16}\t{Field_01}\t{Field_02}\t{Field_03}\t{Field_04}"; +} \ No newline at end of file diff --git a/pkNX.Structures.FlatBuffers/Arceus/Throw/ThrowableParamTable8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Throw/ThrowableParamTable8a.cs new file mode 100644 index 00000000..dedf4317 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Throw/ThrowableParamTable8a.cs @@ -0,0 +1,35 @@ +using System; +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ThrowableParamTable8a : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public ThrowableParam8a[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class ThrowableParam8a +{ + [FlatBufferItem(00)] public int ItemID { get; set; } + [FlatBufferItem(01)] public ulong Hash_01 { get; set; } + [FlatBufferItem(02)] public ulong Hash_02 { get; set; } + [FlatBufferItem(03)] public ulong Hash_03 { get; set; } + [FlatBufferItem(04)] public ulong Hash_04 { get; set; } + [FlatBufferItem(05)] public int Field_05 { get; set; } + [FlatBufferItem(06)] public float Field_06 { get; set; } + [FlatBufferItem(07)] public string Label_01 { get; set; } = string.Empty; + [FlatBufferItem(08)] public string Label_02 { get; set; } = string.Empty; + [FlatBufferItem(09)] public float Field_09 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/TrData8a.cs b/pkNX.Structures.FlatBuffers/Arceus/TrData8a.cs new file mode 100644 index 00000000..15d16af2 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/TrData8a.cs @@ -0,0 +1,88 @@ +using System; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class TrData8a +{ + [FlatBufferItem(00)] public ulong Hash_00 { get; set; } // ulong + [FlatBufferItem(01)] public ulong Hash_01 { get; set; } // ulong + [FlatBufferItem(02)] public ulong Hash_02 { get; set; } // ulong + [FlatBufferItem(03)] public string Music { get; set; } = string.Empty; + [FlatBufferItem(04)] public string EE_04 { get; set; } = string.Empty; + [FlatBufferItem(05)] public string EE_05 { get; set; } = string.Empty; + [FlatBufferItem(06)] public string EE_06 { get; set; } = string.Empty; + [FlatBufferItem(07)] public ulong Hash_07 { get; set; } // ulong + [FlatBufferItem(08)] public ulong Hash_08 { get; set; } // ulong + [FlatBufferItem(09)] public TrFloatQuad Field_09 { get; set; } = new(); + [FlatBufferItem(10)] public int Field_10 { get; set; } // int + [FlatBufferItem(11)] public int Field_11 { get; set; } // int + [FlatBufferItem(12)] public int Field_12 { get; set; } // int + [FlatBufferItem(13)] public byte Field_13 { get; set; } // UNUSED? + [FlatBufferItem(14)] public byte Field_14 { get; set; } // UNUSED? + [FlatBufferItem(15)] public byte Field_15 { get; set; } // UNUSED? + [FlatBufferItem(16)] public byte Field_16 { get; set; } // byte + [FlatBufferItem(17)] public byte Field_17 { get; set; } // byte + [FlatBufferItem(18)] public byte Field_18 { get; set; } // byte + [FlatBufferItem(19)] public byte Field_19 { get; set; } // UNUSED? + [FlatBufferItem(20)] public byte Field_20 { get; set; } // byte + [FlatBufferItem(21)] public byte Field_21 { get; set; } // byte + [FlatBufferItem(22)] public TrPoke8a[] Team { get; set; } = Array.Empty(); + + public string TeamSummary => Environment.NewLine + string.Join(Environment.NewLine, Team.Select(z => z.ToString())); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class TrPoke8a +{ + [FlatBufferItem(00)] public int Species { get; set; } + [FlatBufferItem(01)] public int Form { get; set; } + [FlatBufferItem(02)] public TrPoke8aMove Move_01 { get; set; } = new(); + [FlatBufferItem(03)] public TrPoke8aMove Move_02 { get; set; } = new(); + [FlatBufferItem(04)] public TrPoke8aMove Move_03 { get; set; } = new(); + [FlatBufferItem(05)] public TrPoke8aMove Move_04 { get; set; } = new(); + [FlatBufferItem(06)] public int Field_06 { get; set; } + [FlatBufferItem(07)] public int Field_07 { get; set; } + [FlatBufferItem(08)] public int Field_08 { get; set; } + [FlatBufferItem(09)] public int Field_09 { get; set; } + [FlatBufferItem(10)] public int Field_10 { get; set; } + [FlatBufferItem(11)] public int Field_11 { get; set; } + [FlatBufferItem(12)] public int Field_12 { get; set; } + [FlatBufferItem(13)] public int Field_13 { get; set; } + [FlatBufferItem(14)] public int Field_14 { get; set; } + [FlatBufferItem(15)] public int Field_15 { get; set; } + + public override string ToString() + { + return $"{Species}{(Form == 0 ? "" : $"-{Form}")}|{Move_01}|{Move_02}|{Move_03}|{Move_04}|{Field_06}|{Field_07}|{Field_08}|{Field_09}|{Field_10}|{Field_11}|{Field_12}|{Field_13}|{Field_14}|{Field_15}"; + } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class TrPoke8aMove +{ + [FlatBufferItem(00)] public int Move { get; set; } + [FlatBufferItem(01)] public bool Flag { get; set; } + public override string ToString() => $"{Move}{(Flag?"*":"")}"; +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class TrFloatQuad +{ + [FlatBufferItem(00)] public float Float_00 { get; set; } + [FlatBufferItem(01)] public float Float_01 { get; set; } + [FlatBufferItem(02)] public float Float_02 { get; set; } + [FlatBufferItem(03)] public float Float_03 { get; set; } + + public override string ToString() => $"({Float_00},{Float_01},{Float_02},{Float_03})"; +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/AbilityType8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/AbilityType8a.cs new file mode 100644 index 00000000..d651f921 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/AbilityType8a.cs @@ -0,0 +1,15 @@ +using FlatSharp.Attributes; + +// ReSharper disable UnusedMember.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferEnum(typeof(int))] +public enum AbilityType8a +{ + Any12 = 0, + Any12H = 1, + Only1 = 2, + Only2 = 3, + OnlyH = 4, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/EncounterDetail8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/EncounterDetail8a.cs new file mode 100644 index 00000000..580a1d6d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/EncounterDetail8a.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Linq; + +namespace pkNX.Structures.FlatBuffers; + +public record struct EncounterDetail8a(double Rate, double MultT, double MultW, int Unk, EncounterSlot8a Slot) +{ + private const int WeatherCount = (int)Weather8a.Count; + private const int TimeCount = (int)Time8a.Count; + + public static List[,] GetEmpty() + { + var result = new List[TimeCount, WeatherCount]; + for (var t = 0; t < TimeCount; t++) + { + for (var w = 0; w < WeatherCount; w++) + result[t, w] = new List(); + } + return result; + } + + public static void Divide(List[,] table) + { + for (var time = 0; time < TimeCount; time++) + { + for (var weather = 0; weather < WeatherCount; weather++) + { + table[time, weather] = table[time, weather].OrderByDescending(xt => xt.Rate).ToList(); + + var totalRate = 0.0; + foreach (var tup in table[time, weather]) + totalRate += tup.Rate; + + for (var i = 0; i < table[time, weather].Count; i++) + { + var e = table[time, weather][i]; + var effective = (e.Rate / totalRate) * 100.0; + var detail = new EncounterDetail8a(effective, e.MultT, e.MultW, e.Unk, e.Slot); + table[time, weather][i] = detail; + } + } + } + } + public static TableSymmetryResult AnalyzeSymmetry(List[,] table) + { + var isWeatherSymmetric = true; + var isTimeSymmetric = true; + var weatherSymmetricPerTime = new[] { true, true, true, true }; + for (var time = 0; time < 4; time++) + { + for (var weather = 0; weather < (int)Weather8a.Count; weather++) + { + var ts = IsSameEffectiveTable(table[time, weather], table[0, weather]); + var ws = IsSameEffectiveTable(table[time, weather], table[time, 0]); + isTimeSymmetric &= ts; + isWeatherSymmetric &= ws; + weatherSymmetricPerTime[time] &= ws; + } + } + + return new TableSymmetryResult(isWeatherSymmetric, isTimeSymmetric, weatherSymmetricPerTime); + } + + private static bool IsSameEffectiveTable(IReadOnlyList lhs, IReadOnlyList rhs) + { + if (lhs.Count != rhs.Count) return false; + + for (var i = 0; i < lhs.Count; i++) + { + if (lhs[i].Rate != rhs[i].Rate) return false; + if (lhs[i].Unk != rhs[i].Unk) return false; + } + + return true; + } +} + +public record struct TableSymmetryResult(bool Weather, bool Time, bool[] Complexed); \ No newline at end of file diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/EncounterTable8aUtil.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/EncounterTable8aUtil.cs new file mode 100644 index 00000000..8079451d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/EncounterTable8aUtil.cs @@ -0,0 +1,423 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace pkNX.Structures.FlatBuffers +{ + public static class EncounterTable8aUtil + { + private const float SpawnerBias = 73; // lured unown and jet + run + private const float WormholeBias = 15; + private const float LandmarkBias = 15; + + public static IEnumerable GetEncounterDump(AreaInstance8a area, + IReadOnlyDictionary map, PokeMiscTable8a misc) + { + if (area.Locations.Length == 0) + yield break; + + foreach (var table in area.Encounters.Table) + { + var slots = table.Table.Where(z => z.ShinyLock is ShinyType8a.Random).ToList(); + if (slots.Count == 0) + continue; + + foreach (var p in GetAreas(area, slots, table, map, misc)) + yield return p; + foreach (var x in area.SubAreas) + { + foreach (var p in GetAreas(x, slots, table, map, misc)) + yield return p; + } + } + } + + private static IEnumerable GetAreas(AreaInstance8a area, IReadOnlyCollection slots, EncounterTable8a table, IReadOnlyDictionary map, PokeMiscTable8a misc) + { + if (area.Locations.Length == 0) + yield break; + + int baseArea = map[area.Locations.First(z => z.IsNamedPlace).PlaceName].Index; + // Spawners + { + var s = area.Spawners; + var spawners = s.Where(z => z.UsesTable(table.TableID)); + var sl = spawners.SelectMany(z => z.GetIntersectingLocations(area.Locations, SpawnerBias)); + foreach (var a in GetAll(sl, SpawnerType.Spawner)) + yield return a; + } + + // Wormholes + { + var s = area.Wormholes; + var spawners = s.Where(z => z.UsesTable(table.TableID)); + + // Since Wormholes can have different bonus level ranges, we defer uniqueness testing to the outer method. Just yield all spawners. + foreach (var w in spawners) + { + var bmin = w.Field_20_Value.BonusLevelMin; + var bmax = w.Field_20_Value.BonusLevelMax; + + var sl = w.GetIntersectingLocations(area.Locations, WormholeBias); + foreach (var a in GetAll(sl, SpawnerType.Wormhole, bmin, bmax)) + yield return a; + } + } + + // Landmarks + { + var items = area.LandItems; + var lis = items.Where(z => z.UsesTable(table.TableID)).ToList(); + + var marks = area.LandMarks; + var li = marks.Where(z => lis.Any(sz => z.UsesTable(sz.LandmarkItemSpawnTableID))); + var sl = li.SelectMany(z => z.GetIntersectingLocations(area.Locations, LandmarkBias)); + foreach (var a in GetAll(sl, SpawnerType.Landmark)) + yield return a; + } + + IEnumerable GetAll(IEnumerable places, SpawnerType type, int bmin = 0, int bmax = 0) + { + var temp = places.Select(z => z.PlaceName); + var areas = temp.Select(z => map[z].Index).Distinct().ToList(); + if (areas.Count == 0) + yield break; + if (areas.Remove(baseArea) && areas.Count == 0) + areas.Add(baseArea); + + foreach (var a in areas) + { + var parent = baseArea; + if (IsDungeonZone(a)) + parent = a; // disallow crossover-out for dungeons + yield return GetArea(a, parent, slots, table.MinLevel, table.MaxLevel, type, misc, bmin, bmax); + } + } + } + + // 064 Seaside Hollow + // 086 Wayward Cave + // 095 Snowpoint Temple + private static bool IsDungeonZone(int a) => a is 64 or 86 or 95; + + private static readonly int[] OybnSettings = { 15, 15, 15, 20, 20 }; + + private static byte[] GetArea(int location, int parentArea, IReadOnlyCollection slots, + int tableMinLevel, int tableMaxLevel, SpawnerType type, + PokeMiscTable8a misc, + int bonusMin = 0, + int bonusMax = 0) + { + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms); + bw.Write((byte)location); + bw.Write((byte)parentArea); + bw.Write((byte)type); + bw.Write((byte)slots.Count); + foreach (var s in slots) + { + var (_, min, max) = s.GetLevels(tableMinLevel, tableMaxLevel); + min += bonusMin; + max += bonusMax; + int alpha = 0; + var oybn = s.Oybn; + if (oybn.Oybn1 || oybn.Oybn2) + { + var miscEntry = misc.GetEntry(s.Species, s.Form); + var boostIndex = miscEntry.OybnLevelIndex; + var boost = OybnSettings[boostIndex - 1]; + max += boost; + min += boost; + if (s.Oybn.Oybn2) + alpha = 2; // Oybn2 -- Master All Moves Possible? + else if (s.Oybn.Oybn1) + alpha = 1; // Oybn1 -- Master only Alpha move? + } + + var gender = s.Gender == -1 ? 2 : s.Gender; + bw.Write((ushort)s.Species); + bw.Write((byte)s.Form); + bw.Write((byte)alpha); + bw.Write((byte)min); + bw.Write((byte)max); + bw.Write((byte)gender); + bw.Write((byte)s.NumPerfectIvs); + } + return ms.ToArray(); + } + + public static IEnumerable GetUnownLines(AreaInstance8a area, IReadOnlyDictionary map) + { + yield return $"Area: {area.AreaName}"; + foreach (var u in area.Unown.Concat(area.SubAreas.SelectMany(x => x.Unown))) + { + var contained = u.GetContainingLocations(area.Locations).First().PlaceName; + var name = map[contained].Name; + var p = u.Parameters; + yield return $"Unown {u.Identifier} @ {u.Hash_01:X16}_{u.Hash_03:X16} {u.Flag} ({p.GetConditionSummary()}) @ {p.Coordinates.ToTriple()}, {area.AreaName} = {name}"; + } + } + + public static IEnumerable GetUnownLinesBias(AreaInstance8a area, IReadOnlyDictionary map, float bias) + { + yield return $"Area: {area.AreaName}"; + foreach (var u in area.Unown.Concat(area.SubAreas.SelectMany(x => x.Unown))) + { + var contained = u.GetIntersectingLocations(area.Locations, bias); + foreach (var c in contained) + { + var name = map[c.PlaceName].Name; + var p = u.Parameters; + yield return $"Unown {u.Identifier} @ {u.Hash_01:X16}_{u.Hash_03:X16} {u.Flag} ({p.GetConditionSummary()}) @ {p.Coordinates.ToTriple()}, {area.AreaName} = {name}"; + } + } + } + + public static IEnumerable GetLines(EncounterMultiplerArchive8a multiplier_archive, + PokeMiscTable8a misc, string[] speciesNames, + AreaInstance8a area, IReadOnlyDictionary map) + { + yield return $"Area: {area.AreaName}"; + + foreach (var enctable in area.Encounters.Table) { + foreach (var line in GetTableSummary(enctable, multiplier_archive, speciesNames, misc, area, map)) + yield return $"\t{line}"; + + yield return string.Empty; + } + } + + private static IEnumerable GetUsedSpawnerSummary(EncounterTable8a t, AreaInstance8a area, IReadOnlyDictionary valueTuples) + { + var usedBySpawners = Array.FindAll(area.Spawners, z => z.UsesTable(t.TableID)); + var usedByWormholes = Array.FindAll(area.Wormholes, z => z.UsesTable(t.TableID)); + var usedByLandmarkSpawns = Array.FindAll(area.LandItems, z => z.UsesTable(t.TableID)); + var usedByLandmarks = Array.FindAll(area.LandMarks, z => usedByLandmarkSpawns.Any(sz => z.UsesTable(sz.LandmarkItemSpawnTableID))); + + foreach (var s in usedBySpawners) + { + var contained = s.GetContainingLocations(area.Locations).First().PlaceName; + var name = valueTuples[contained].Name; + var p = s.Parameters; + yield return $"Spawner @ {s.NameSummary}_{s.Field_01:X16} ({p.GetConditionSummary()}) @ {p.Coordinates.ToTriple()}, {area.AreaName} = {name}"; + } + foreach (var s in usedByWormholes) + { + var contained = s.GetContainingLocations(area.Locations).First().PlaceName; + var name = valueTuples[contained].Name; + var p = s.Parameters; + var c = s.Field_20_Value; + yield return $"Wormhole: {s.NameSummary}_{s.Field_01:X16} ({p.GetConditionSummary()}) [{c.BonusLevelMin}-{c.BonusLevelMax}] @ {p.Coordinates.ToTriple()}, {area.AreaName} = {name}"; + } + foreach (var s in usedByLandmarks) + { + var contained = s.GetContainingLocations(area.Locations).First().PlaceName; + var name = valueTuples[contained].Name; + var spawn = usedByLandmarkSpawns.First(sz => s.UsesTable(sz.LandmarkItemSpawnTableID)); + var p = s.Parameters; + yield return $"Landmark: {s.NameSummary}_{s.Field_01:X16}_{spawn.NameSummary} ({p.GetConditionSummary()}) @ {p.Coordinates.ToTriple()}, {area.AreaName} = {name}"; + } + } + + private static IEnumerable GetTableSummary(EncounterTable8a t, + EncounterMultiplerArchive8a multiplier_archive, string[] speciesNames, + PokeMiscTable8a misc, + AreaInstance8a area, IReadOnlyDictionary valueTuples) + { + yield return $"{t}:"; + + var totalUses = 0; + foreach (var line in GetUsedSpawnerSummary(t, area, valueTuples)) + { + totalUses++; + yield return $"\t{line}"; + } + + foreach (var subArea in area.SubAreas) + { + foreach (var line in GetUsedSpawnerSummary(t, subArea, valueTuples)) + { + totalUses++; + yield return $"\t{line}"; + } + } + + if (totalUses == 0) + { + yield return "\tNo spawners? Check that this is used somewhere."; + } + + foreach (var line in GetLines(t.Table, multiplier_archive, speciesNames, t.MinLevel, t.MaxLevel, misc)) + yield return $"\t{line}"; + } + + private static IEnumerable GetLines(IReadOnlyList arr, + EncounterMultiplerArchive8a multiplier_archive, IReadOnlyList speciesNames, int lvMin, int lvMax, + PokeMiscTable8a misc) + { + var dividedTables = EncounterDetail8a.GetEmpty(); + FillSlots(arr, multiplier_archive, dividedTables); + EncounterDetail8a.Divide(dividedTables); + + var sym = EncounterDetail8a.AnalyzeSymmetry(dividedTables); + if (sym.Time && sym.Weather) + { + yield return "Any Time/All Weather:"; + foreach (var line in GetEffectiveTableSummary(dividedTables[0, 0], speciesNames, lvMin, lvMax, misc)) + yield return $"\t{line}"; + } + else if (sym.Weather) + { + for (var time = 0; time < dividedTables.GetLength(0); time++) + { + yield return $"{(Time8a)time}/All Weather:"; + foreach (var line in GetEffectiveTableSummary(dividedTables[time, 0], speciesNames, lvMin, lvMax, misc)) + yield return $"\t{line}"; + } + } + else if (sym.Time) + { + for (var weather = 0; weather < dividedTables.GetLength(1); weather++) + { + yield return $"Any Time/{(Weather8a)weather}:"; + foreach (var line in GetEffectiveTableSummary(dividedTables[0, weather], speciesNames, lvMin, lvMax, misc)) + yield return $"\t{line}"; + } + } + else + { + for (var time = 0; time < dividedTables.GetLength(0); time++) + { + if (sym.Complexed[time]) + { + yield return $"{(Time8a)time}/All Weather:"; + foreach (var line in GetEffectiveTableSummary(dividedTables[time, 0], speciesNames, lvMin, lvMax, misc)) + yield return $"\t{line}"; + } + else + { + for (var weather = 0; weather < dividedTables.GetLength(1); weather++) + { + yield return $"{(Time8a)time}/{(Weather8a)weather}:"; + foreach (var line in GetEffectiveTableSummary(dividedTables[time, weather], speciesNames, lvMin, lvMax, misc)) + yield return $"\t{line}"; + } + } + } + } + } + + private static void FillSlots(IEnumerable arr, EncounterMultiplerArchive8a multArchive, List[,] dividedTables) + { + var ctr = 0; + foreach (var slot in arr.OrderByDescending(sl => sl.BaseProbability)) + { + var defaults = multArchive.GetEncounterMultiplier(slot); + for (var time = 0; time < dividedTables.GetLength(0); time++) + { + var MultT = slot.GetTimeModifier(time, defaults); + for (var weather = 0; weather < dividedTables.GetLength(1); weather++) + { + var MultW = slot.GetWeatherModifier(weather, defaults); + var rate = slot.BaseProbability * MultT * MultW; + if (rate == 0) + continue; + + var detail = new EncounterDetail8a(slot.BaseProbability * MultT * MultW, MultT, MultW, ctr, slot); + dividedTables[time, weather].Add(detail); + } + } + + ctr++; + } + } + + private static IEnumerable GetEffectiveTableSummary(IReadOnlyList table, IReadOnlyList speciesNames, int lvMin, int lvMax, PokeMiscTable8a misc) + { + if (table.Count == 0) + { + yield return " - None"; + yield break; + } + + foreach (var tup in table) + { + var rate = tup.Rate; + var slot = tup.Slot; + + string form = slot.Form == 0 ? string.Empty : $"-{slot.Form}"; + var spec_form = $"{speciesNames[slot.Species]}{form}"; + + var summary = $"- {rate:00.00}%\t{spec_form,-12}"; + + var (force, min, max) = slot.GetLevels(lvMin, lvMax); + if (slot.Oybn.Oybn1 || slot.Oybn.Oybn2) + { + var miscEntry = misc.GetEntry(slot.Species, slot.Form); + var boostIndex = miscEntry.OybnLevelIndex; + var boost = OybnSettings[boostIndex - 1]; + max += boost; + min += boost; + summary += $"\tAlphaLevel={min}-{max}"; + } + else if (force) + { + summary += $"\tOverrideLevel={min}-{max}"; + } + + if (slot.Gender != -1) + summary += $"\tGender={slot.Gender}"; + + if (slot.ShinyLock is not ShinyType8a.Random) + summary += $"\tShinyLock={slot.ShinyLock}"; + + if (slot.AbilityRandType is not AbilityType8a.Any12) + summary += $"\tAbility={slot.AbilityRandType}"; + + if (slot.Nature is not NatureType8a.Random) + summary += $"\tNature={slot.Nature}"; + + var gvs = new[] { slot.GV_HP, slot.GV_ATK, slot.GV_DEF, slot.GV_SPA, slot.GV_SPD, slot.GV_SPE }; + if (gvs.Any(x => x != -1)) + summary += $"\tGVS={string.Join("/", gvs.Select(v => v != -1 ? v.ToString() : "*").ToArray())}"; + + if (slot.NumPerfectIvs != 0) + summary += $"\tPerfectIvs={slot.NumPerfectIvs}"; + + var ivs = new[] { slot.IV_HP, slot.IV_ATK, slot.IV_DEF, slot.IV_SPA, slot.IV_SPD, slot.IV_SPE }; + if (ivs.Any(x => x != -1)) + summary += $"\tIVS={string.Join("/", ivs.Select(v => v != -1 ? v.ToString() : "*").ToArray())}"; + + var oybn = slot.Oybn; + if (oybn.IsOybnAny) + { + if (oybn.Oybn1 && !oybn.Oybn2 && oybn.Field_02 && oybn.Field_03) + summary += "\tOybn=Type1"; + else if (oybn.Oybn1 && oybn.Oybn2 && oybn.Field_02 && oybn.Field_03) + summary += "\tOybn=Type2"; + else + summary += $"\tOybn={{{(oybn.Oybn1 ? 1 : 0)},{(oybn.Oybn2 ? 1 : 0)},{(oybn.Field_02 ? 1 : 0)},{(oybn.Field_03 ? 1 : 0)}}}"; + } + + var elg = slot.Eligibility; + if (slot.Eligibility.ConditionTypeID != ConditionType8a.None) + { + summary += $"\tConditionType={elg.GetConditionTypeSummary()}"; + summary += $"\tCondition={elg.GetConditionSummary()}"; + } + + if (!string.IsNullOrEmpty(slot.Behavior1)) + summary += $"\t{nameof(slot.Behavior1)}=\"{slot.Behavior1}\""; + + if (!string.IsNullOrEmpty(slot.Behavior2)) + summary += $"\t{nameof(slot.Behavior2)}=\"{slot.Behavior2}\""; + + if (slot.SlotID != 0xCBF29CE484222645) + summary += $"\tSlotID={slot.SlotName}"; + + yield return summary; + } + } + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/IHasCondition8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/IHasCondition8a.cs new file mode 100644 index 00000000..6b6a80c9 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/IHasCondition8a.cs @@ -0,0 +1,14 @@ +// ReSharper disable UnusedMember.Global + +namespace pkNX.Structures.FlatBuffers; + +public interface IHasCondition8a +{ + ConditionType8a ConditionTypeID { get; set; } + Condition8a ConditionID { get; set; } + string ConditionArg1 { get; set; } + string ConditionArg2 { get; set; } + string ConditionArg3 { get; set; } + string ConditionArg4 { get; set; } + string ConditionArg5 { get; set; } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/NatureType8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/NatureType8a.cs new file mode 100644 index 00000000..faa08fdf --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/NatureType8a.cs @@ -0,0 +1,41 @@ +using FlatSharp.Attributes; + +// ReSharper disable UnusedMember.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferEnum(typeof(int))] +public enum NatureType8a +{ + Random = -1, + + Hardy, + Lonely, + Brave, + Adamant, + Naughty, + Bold, + + Docile, + Relaxed, + Impish, + Lax, + Timid, + Hasty, + + Serious, + Jolly, + Naive, + Modest, + Mild, + Quiet, + + Bashful, + Rash, + Calm, + Gentle, + Sassy, + Careful, + + Quirky, +} \ No newline at end of file diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/ShinyType8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/ShinyType8a.cs new file mode 100644 index 00000000..e824026d --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/ShinyType8a.cs @@ -0,0 +1,13 @@ +using FlatSharp.Attributes; + +// ReSharper disable UnusedMember.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferEnum(typeof(int))] +public enum ShinyType8a +{ + Random = 0, + Always = 1, + Never = 2, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/SpawnerType.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/SpawnerType.cs new file mode 100644 index 00000000..5f3290d1 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/SpawnerType.cs @@ -0,0 +1,9 @@ +namespace pkNX.Structures.FlatBuffers; + +public enum SpawnerType +{ + Spawner, + Wormhole, + Landmark, + Unown, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/Time8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/Time8a.cs new file mode 100644 index 00000000..9187deb2 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/Time8a.cs @@ -0,0 +1,12 @@ +// ReSharper disable UnusedMember.Global + +namespace pkNX.Structures.FlatBuffers; + +public enum Time8a +{ + Dawn = 0, + Day = 1, + Dusk = 2, + Night = 3, + Count = 4, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Util/Weather8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Util/Weather8a.cs new file mode 100644 index 00000000..3cc4f169 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Util/Weather8a.cs @@ -0,0 +1,17 @@ +namespace pkNX.Structures.FlatBuffers; + +// ReSharper disable UnusedMember.Global + +public enum Weather8a +{ + None = 0, + Sunny = 1, + Cloudy = 2, + Rain = 3, + Snow = 4, + Drought = 5, + Fog = 6, + Rainstorm = 7, + Snowstorm = 8, + Count = 9, +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/Waza8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Waza8a.cs new file mode 100644 index 00000000..fd5f4c31 --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Waza8a.cs @@ -0,0 +1,157 @@ +using System.ComponentModel; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class Waza8a : IMove +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + // Type mismatch; FlatBuffer must use the correct struct type for each field + // We need to alias these and hide them from any PropertyGrid, so mark Browsable(false). + + [FlatBufferItem(00)] public int MoveID { get; set; } // int + [FlatBufferItem(01)] public bool CanUseMove { get; set; } + [FlatBufferItem(02), Browsable(false)] public byte FType { get; set; } // byte + [FlatBufferItem(03), Browsable(false)] public byte FQuality { get; set; } // byte + [FlatBufferItem(04), Browsable(false)] public byte FCategory { get; set; } // byte + [FlatBufferItem(05), Browsable(false)] public byte FPower { get; set; } // byte + [FlatBufferItem(06), Browsable(false)] public byte FAccuracy { get; set; } // byte + [FlatBufferItem(07), Browsable(false)] public byte FPP { get; set; } // byte + [FlatBufferItem(08), Browsable(false)] public sbyte FPriority { get; set; } // byte + [FlatBufferItem(09), Browsable(false)] public byte FHitMin { get; set; } // byte + [FlatBufferItem(10), Browsable(false)] public byte FHitMax { get; set; } // byte + [FlatBufferItem(11), Browsable(false)] public short FInflict { get; set; } // ushort + [FlatBufferItem(12), Browsable(false)] public byte FInflictPercent { get; set; } // byte + [FlatBufferItem(13), Browsable(false)] public byte FRawInflictCount { get; set; } // byte + [FlatBufferItem(14), Browsable(false)] public byte FTurnMin { get; set; } // byte + [FlatBufferItem(15), Browsable(false)] public byte FTurnMax { get; set; } // byte + [FlatBufferItem(16), Browsable(false)] public byte FCritStage { get; set; } // byte + [FlatBufferItem(17), Browsable(false)] public byte FFlinch { get; set; } // byte + [FlatBufferItem(18), Browsable(false)] public ushort FEffectSequence { get; set; } // ushort + [FlatBufferItem(19), Browsable(false)] public byte FRecoil { get; set; } // byte + [FlatBufferItem(20), Browsable(false)] public byte FRawHealing { get; set; } // byte + [FlatBufferItem(21), Browsable(false)] public byte FRawTarget { get; set; } // byte + [FlatBufferItem(22), Browsable(false)] public byte FStat1 { get; set; } // byte + [FlatBufferItem(23), Browsable(false)] public byte FStat2 { get; set; } // byte + [FlatBufferItem(24), Browsable(false)] public byte FStat3 { get; set; } // byte + [FlatBufferItem(25), Browsable(false)] public sbyte FStat1Stage { get; set; } // byte + [FlatBufferItem(26), Browsable(false)] public sbyte FStat2Stage { get; set; } // byte + [FlatBufferItem(27), Browsable(false)] public sbyte FStat3Stage { get; set; } // byte + [FlatBufferItem(28), Browsable(false)] public byte FStat1Percent { get; set; } // byte + [FlatBufferItem(29), Browsable(false)] public byte FStat2Percent { get; set; } // byte + [FlatBufferItem(30), Browsable(false)] public byte FStat3Percent { get; set; } // byte + [FlatBufferItem(31)] public byte FStat1Duration { get; set; } // byte + [FlatBufferItem(32)] public byte FStat2Duration { get; set; } // byte + [FlatBufferItem(33)] public byte FStat3Duration { get; set; } // byte + [FlatBufferItem(34)] public byte GigantamaxPower { get; set; } // byte + [FlatBufferItem(35)] public bool Flag_MakesContact { get; set; } + [FlatBufferItem(36)] public bool Flag_Charge { get; set; } + [FlatBufferItem(37)] public bool Flag_Recharge { get; set; } + [FlatBufferItem(38)] public bool Flag_Protect { get; set; } + [FlatBufferItem(39)] public bool Flag_Reflectable { get; set; } + [FlatBufferItem(40)] public bool Flag_Snatch { get; set; } + [FlatBufferItem(41)] public bool Flag_Mirror { get; set; } + [FlatBufferItem(42)] public bool Flag_Punch { get; set; } + [FlatBufferItem(43)] public bool Flag_Sound { get; set; } + [FlatBufferItem(44)] public bool Flag_Gravity { get; set; } + [FlatBufferItem(45)] public bool Flag_Defrost { get; set; } + [FlatBufferItem(46)] public bool Flag_DistanceTriple { get; set; } + [FlatBufferItem(47)] public bool Flag_Heal { get; set; } + [FlatBufferItem(48)] public bool Flag_IgnoreSubstitute { get; set; } + [FlatBufferItem(49)] public bool Flag_FailSkyBattle { get; set; } + [FlatBufferItem(50)] public bool Flag_AnimateAlly { get; set; } + [FlatBufferItem(51)] public bool Flag_Dance { get; set; } + [FlatBufferItem(52)] public bool Flag_Metronome { get; set; } + // New additions! + [FlatBufferItem(53)] public byte SplinterModifier { get; set; } // byte + [FlatBufferItem(54)] public byte Status_Fixated { get; set; } // byte + [FlatBufferItem(55)] public byte Status_Obscured { get; set; } // byte + [FlatBufferItem(56)] public byte Status_ObscuredDuration { get; set; } // byte + [FlatBufferItem(57)] public byte Status_Primed { get; set; } // byte + [FlatBufferItem(58)] public byte Status_PrimedDuration { get; set; } // byte + [FlatBufferItem(59)] public byte Status_PrimedPercent { get; set; } // byte + [FlatBufferItem(60)] public byte Status_StanceSwap { get; set; } // byte + [FlatBufferItem(61)] public byte Status_StanceSwapDuration { get; set; } // byte + [FlatBufferItem(62)] public byte Status_FutureCrit { get; set; } // byte + [FlatBufferItem(63)] public byte Status_FutureCritDuration { get; set; } // byte + [FlatBufferItem(64)] public byte Status_FutureCritStage { get; set; } // byte + [FlatBufferItem(65)] public bool Flag_CureDrowsy { get; set; } // byte + [FlatBufferItem(66)] public bool Flag_CureFrostbite { get; set; } // byte + [FlatBufferItem(67)] public int DamagePercentStatused { get; set; } // int + [FlatBufferItem(68)] public byte CanStyle { get; set; } // byte + [FlatBufferItem(69)] public int ActionSpeedMod { get; set; } // int + [FlatBufferItem(70)] public int AgileActionSpeedMod { get; set; } // int + [FlatBufferItem(71)] public int StrongActionSpeedMod { get; set; } // int + [FlatBufferItem(72)] public int TargetActionSpeedMod { get; set; } // int + [FlatBufferItem(73)] public int StrongTargetActionSpeedMod { get; set; } // int + [FlatBufferItem(74)] public byte AgilePower { get; set; } // byte + [FlatBufferItem(75)] public byte AgileRawHealing { get; set; } // byte + [FlatBufferItem(76)] public byte AgileTurnMax { get; set; } // byte + [FlatBufferItem(77)] public byte AgileEffectDuration { get; set; } // byte + [FlatBufferItem(78)] public byte AgileStatChangeDuration { get; set; } // byte + [FlatBufferItem(79)] public byte StrongPower { get; set; } // byte + [FlatBufferItem(80)] public byte StrongAccuracy { get; set; } // byte + [FlatBufferItem(81)] public byte StrongCritStage { get; set; } // byte + [FlatBufferItem(82)] public byte StrongInflictPercent { get; set; } // byte + [FlatBufferItem(83)] public byte StrongStat1Percent { get; set; } // byte + [FlatBufferItem(84)] public byte StrongTurnMax { get; set; } // byte + [FlatBufferItem(85)] public byte StrongEffectDuration { get; set; } // byte + [FlatBufferItem(86)] public byte StrongStatChangeDuration { get; set; } // byte + [FlatBufferItem(87)] public int StrongRecoil { get; set; } // int + [FlatBufferItem(88)] public byte StrongRawHealing { get; set; } // byte + [FlatBufferItem(89)] public byte StrongSplinterModifier { get; set; } // byte + + public int Type { get => FType; set => FType = (byte)value; } + public int Quality { get => FQuality ; set => FQuality = (byte)value; } + public int Category { get => FCategory ; set => FCategory = (byte)value; } + public int Power { get => FPower ; set => FPower = (byte)value; } + public int Accuracy { get => FAccuracy ; set => FAccuracy = (byte)value; } + public int PP { get => FPP ; set => FPP = (byte)value; } + public int Priority { get => FPriority ; set => FPriority = (sbyte)value; } + public int HitMin { get => FHitMin ; set => FHitMin = (byte)value; } + public int HitMax { get => FHitMax ; set => FHitMax = (byte)value; } + public int Inflict { get => FInflict ; set => FInflict = (short)value; } + public int InflictPercent { get => FInflictPercent ; set => FInflictPercent = (byte)value; } + public int TurnMin { get => FTurnMin ; set => FTurnMin = (byte)value; } + public int TurnMax { get => FTurnMax ; set => FTurnMax = (byte)value; } + public int CritStage { get => FCritStage ; set => FCritStage = (byte)value; } + public int Flinch { get => FFlinch ; set => FFlinch = (byte)value; } + public int EffectSequence { get => FEffectSequence ; set => FEffectSequence = (ushort)value; } + public int Recoil { get => FRecoil ; set => FRecoil = (byte)value; } + public int Stat1 { get => FStat1 ; set => FStat1 = (byte)value; } + public int Stat2 { get => FStat2 ; set => FStat2 = (byte)value; } + public int Stat3 { get => FStat3 ; set => FStat3 = (byte)value; } + public int Stat1Stage { get => FStat1Stage ; set => FStat1Stage = (sbyte)value; } + public int Stat2Stage { get => FStat2Stage ; set => FStat2Stage = (sbyte)value; } + public int Stat3Stage { get => FStat3Stage ; set => FStat3Stage = (sbyte)value; } + public int Stat1Percent { get => FStat1Percent ; set => FStat1Percent = (byte)value; } + public int Stat2Percent { get => FStat2Percent ; set => FStat2Percent = (byte)value; } + public int Stat3Percent { get => FStat3Percent ; set => FStat3Percent = (byte)value; } + + public MoveInflictDuration InflictCount + { + get => (MoveInflictDuration)FRawInflictCount; + set => FRawInflictCount = (byte)value; + } + + public Heal Healing + { + get => (Heal)FRawHealing; + set => FRawHealing = (byte)value; + } + + public MoveTarget Target + { + get => (MoveTarget)FRawTarget; + set => FRawTarget = (byte)value; + } +} diff --git a/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterNest8Archive.cs b/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterNest8Archive.cs index 23e6507e..52a4ac84 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterNest8Archive.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterNest8Archive.cs @@ -10,7 +10,6 @@ // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global #nullable disable -#pragma warning disable CA1819 // Properties should not return arrays namespace pkNX.Structures.FlatBuffers { @@ -147,7 +146,7 @@ IEnumerable GetOrderedDrops(IReadOnlyList string GetItemName(uint itemID) { - if (1130 <= itemID && itemID < 1230) // TR + if (itemID is >= 1130 and < 1230) // TR return $"{items[(int) itemID]} {moves[tmtrs[100 + (int) itemID - 1130]]}"; return items[(int) itemID]; } diff --git a/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterUnderground8Archive.cs b/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterUnderground8Archive.cs index a943d66e..b01c49d9 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterUnderground8Archive.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Nest/EncounterUnderground8Archive.cs @@ -7,7 +7,6 @@ // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global #nullable disable -#pragma warning disable CA1819 // Properties should not return arrays namespace pkNX.Structures.FlatBuffers { diff --git a/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleCrystalEncounter8Archive.cs b/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleCrystalEncounter8Archive.cs index b8c37efd..6def4c5c 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleCrystalEncounter8Archive.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleCrystalEncounter8Archive.cs @@ -10,7 +10,6 @@ // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global #nullable disable -#pragma warning disable CA1819 // Properties should not return arrays namespace pkNX.Structures.FlatBuffers { @@ -98,26 +97,21 @@ IEnumerable GetOrderedDrops(IReadOnlyList string GetItemName(uint itemID) { - if (1130 <= itemID && itemID < 1230) // TR + if (itemID is >= 1130 and < 1230) // TR return $"{items[(int)itemID]} {moves[tmtrs[100 + (int)itemID - 1130]]}"; return items[(int)itemID]; } } - private static int GetEncounterRank(int level) + private static int GetEncounterRank(int level) => level switch { - if (15 <= level && level <= 20) - return 1; - if (25 <= level && level <= 30) - return 2; - if (35 <= level && level <= 40) - return 3; - if (45 <= level && level <= 50) - return 4; - if (55 <= level && level <= 60) - return 5; - return 0; - } + >= 15 and <= 20 => 1, + >= 25 and <= 30 => 2, + >= 35 and <= 40 => 3, + >= 45 and <= 50 => 4, + >= 55 and <= 60 => 5, + _ => 0, + }; public IEnumerable GetSummary(IReadOnlyList species, IReadOnlyList items) { diff --git a/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleDistributionEncounter8Archive.cs b/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleDistributionEncounter8Archive.cs index 767d98e1..9fd109ec 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleDistributionEncounter8Archive.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Nest/NestHoleDistributionEncounter8Archive.cs @@ -10,7 +10,6 @@ // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global #nullable disable -#pragma warning disable CA1819 // Properties should not return arrays namespace pkNX.Structures.FlatBuffers { @@ -113,7 +112,7 @@ IEnumerable GetOrderedDrops(IReadOnlyList string GetItemName(uint itemID) { - if (1130 <= itemID && itemID < 1230) // TR + if (itemID is >= 1130 and < 1230) // TR return $"{items[(int)itemID]} {moves[tmtrs[100 + (int)itemID - 1130]]}"; return items[(int)itemID]; } diff --git a/pkNX.Structures.FlatBuffers/Gen8/Other/PokeResourceTable.cs b/pkNX.Structures.FlatBuffers/Gen8/Other/PokeResourceTable.cs index c2c3b71d..0a05ce88 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Other/PokeResourceTable.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Other/PokeResourceTable.cs @@ -1,53 +1,58 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using FlatSharp.Attributes; + // ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable ClassNeverInstantiated.Global // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global -#nullable disable -#pragma warning disable CA1819 // Properties should not return arrays +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global -namespace pkNX.Structures.FlatBuffers +namespace pkNX.Structures.FlatBuffers; + +// poke_resource_table.gfbpmcatalog +/// for Sword/Shield +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeResourceTable : IFlatBufferArchive { - // poke_resource_table.gfbpmcatalog - [FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] - public class PokeResourceTable : IFlatBufferArchive - { - [FlatBufferItem(00)] public PokeResourceMeta Meta { get; set; } - [FlatBufferItem(01)] public PokeModelConfig[] Table { get; set; } - } + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); - [FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] - public class PokeResourceMeta - { - [FlatBufferItem(00)] public int Field0 { get; set; } = 4; - [FlatBufferItem(01)] public int Field1 { get; set; } = 2; - } - - [FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] - public class PokeModelConfig - { - [FlatBufferItem(00)] public PokeModelMeta Meta { get; set; } - [FlatBufferItem(01)] public string PathModel { get; set; } - [FlatBufferItem(02)] public string PathConfig { get; set; } - [FlatBufferItem(03)] public string PathArchive { get; set; } - [FlatBufferItem(04)] public AnimationConfigStringTuple[] Animations { get; set; } - } - - [FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] - public class PokeModelMeta - { - [FlatBufferItem(00)] public ushort Species { get; set; } - [FlatBufferItem(01)] public ushort Form { get; set; } - [FlatBufferItem(02)] public byte Gender { get; set; } - [FlatBufferItem(03)] public byte Shiny { get; set; } - } - - [FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] - public class AnimationConfigStringTuple - { - [FlatBufferItem(00)] public string Name { get; set; } - [FlatBufferItem(01)] public string Path { get; set; } - } + [FlatBufferItem(00)] public PokeResourceMeta Meta { get; set; } = new(); + [FlatBufferItem(01)] public PokeModelConfig[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeResourceMeta +{ + [FlatBufferItem(00)] public int Field0 { get; set; } = 4; + [FlatBufferItem(01)] public int Field1 { get; set; } = 2; +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeModelConfig +{ + [FlatBufferItem(00)] public PokeModelMeta Meta { get; set; } = new(); + [FlatBufferItem(01)] public string PathModel { get; set; } = string.Empty; + [FlatBufferItem(02)] public string PathConfig { get; set; } = string.Empty; + [FlatBufferItem(03)] public string PathArchive { get; set; } = string.Empty; + [FlatBufferItem(04)] public byte Unused { get; set; } // unused + [FlatBufferItem(05)] public AnimationConfigStringTuple[] Base { get; set; } = Array.Empty(); + [FlatBufferItem(06)] public AnimationConfigStringTuple[] Eff { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PokeModelMeta +{ + [FlatBufferItem(00)] public ushort Species { get; set; } + [FlatBufferItem(01)] public ushort Form { get; set; } + [FlatBufferItem(02)] public byte Gender { get; set; } + [FlatBufferItem(03)] public byte Shiny { get; set; } +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class AnimationConfigStringTuple +{ + [FlatBufferItem(00)] public string Name { get; set; } = string.Empty; + [FlatBufferItem(01)] public string Path { get; set; } = string.Empty; } diff --git a/pkNX.Structures.FlatBuffers/Gen8/Placement/Zone/Holders/PlacementZone8SpeciesHolder.cs b/pkNX.Structures.FlatBuffers/Gen8/Placement/Zone/Holders/PlacementZone8SpeciesHolder.cs index f8c3a8cd..5d908248 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Placement/Zone/Holders/PlacementZone8SpeciesHolder.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Placement/Zone/Holders/PlacementZone8SpeciesHolder.cs @@ -6,7 +6,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global -#pragma warning disable CA1819 // Properties should not return arrays namespace pkNX.Structures.FlatBuffers { @@ -110,25 +109,22 @@ public class PlacementZone8_F02 [FlatBufferItem(11)] public FlatDummyObject Field_11 { get; set; } = new(); // no fields present in any existing [FlatBufferItem(12)] public ulong Hash_12 { get; set; } - public PlacementZone8_F02 Clone() + public PlacementZone8_F02 Clone() => new() { - return new() - { - Field_00 = Field_00.Clone(), - Hash_01 = Hash_01, - Hash_02 = Hash_02, - Hash_03 = Hash_03, - Hash_04 = Hash_04, - Field_05 = Field_05, - Field_06 = Field_06, - Field_07 = Field_07, - Field_08 = Field_08, - Field_09 = Field_09.Clone(), - Field_10 = Field_10, - Field_11 = Field_11.Clone(), - Hash_12 = Hash_12, - }; - } + Field_00 = Field_00.Clone(), + Hash_01 = Hash_01, + Hash_02 = Hash_02, + Hash_03 = Hash_03, + Hash_04 = Hash_04, + Field_05 = Field_05, + Field_06 = Field_06, + Field_07 = Field_07, + Field_08 = Field_08, + Field_09 = Field_09.Clone(), + Field_10 = Field_10, + Field_11 = Field_11.Clone(), + Hash_12 = Hash_12, + }; } [FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] diff --git a/pkNX.Structures.FlatBuffers/Gen8/Static/EncounterStatic8Archive.cs b/pkNX.Structures.FlatBuffers/Gen8/Static/EncounterStatic8Archive.cs index e9e444e8..244b19ee 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Static/EncounterStatic8Archive.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Static/EncounterStatic8Archive.cs @@ -5,7 +5,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedType.Global #nullable disable -#pragma warning disable CA1819 // Properties should not return arrays namespace pkNX.Structures.FlatBuffers { diff --git a/pkNX.Structures.FlatBuffers/Gen8/Wild/EncounterTable8Util.cs b/pkNX.Structures.FlatBuffers/Gen8/Wild/EncounterTable8Util.cs index 643d2ca3..6678d314 100644 --- a/pkNX.Structures.FlatBuffers/Gen8/Wild/EncounterTable8Util.cs +++ b/pkNX.Structures.FlatBuffers/Gen8/Wild/EncounterTable8Util.cs @@ -40,7 +40,7 @@ private static DumpableLocation GetDumpable(EncounterTable8 zone, IReadOnlyDicti return DumpableLocation.Empty; // Try to get the table type. Skip inaccessible tables. - if (!zoneType.TryGetValue(zone.ZoneID, out var slottype) || slottype == (byte)SWSHSlotType.Inaccessible) + if (!zoneType.TryGetValue(zone.ZoneID, out var slottype) || slottype == (byte)Inaccessible) return DumpableLocation.Empty; byte locID = tmp; @@ -75,11 +75,11 @@ private static DumpableLocation GetDumpable(EncounterTable8 zone, IReadOnlyDicti private static bool IsPermittedWeather(byte locID, SWSHEncounterType weather, byte slotType) { // Only keep fishing slots for any encounters that are FishingOnly. - if (slotType == (byte)SWSHSlotType.OnlyFishing) + if (slotType == (byte)OnlyFishing) return weather == SWSHEncounterType.Fishing; // Otherwise, keep all fishing and shaking tree encounters. - if (weather == SWSHEncounterType.Shaking_Trees || weather == SWSHEncounterType.Fishing) + if (weather is SWSHEncounterType.Shaking_Trees or SWSHEncounterType.Fishing) return true; // If we didn't find the weather in the general table, only allow Normal. @@ -103,7 +103,7 @@ private static bool IsPermittedWeather(byte locID, SWSHEncounterType weather, by private class DumpableLocation { - public static readonly DumpableLocation Empty = new(new(), 0, 0); + public static readonly DumpableLocation Empty = new(new List(), 0, 0); public readonly List Slots; public readonly byte Location; @@ -150,9 +150,11 @@ private static byte[] SerializeSlot8(byte locID, IEnumerable list, byte s } [Flags] +#pragma warning disable RCS1154 // Sort enum members. private enum SWSHEncounterType +#pragma warning restore RCS1154 // Sort enum members. { - None, + None = 0, Normal = 1, Overcast = 1 << 1, Raining = 1 << 2, diff --git a/pkNX.Structures.FlatBuffers/Util/FlatDumper.cs b/pkNX.Structures.FlatBuffers/Util/FlatDumper.cs index a760f244..96dbda43 100644 --- a/pkNX.Structures.FlatBuffers/Util/FlatDumper.cs +++ b/pkNX.Structures.FlatBuffers/Util/FlatDumper.cs @@ -7,6 +7,11 @@ public static class FlatDumper public static string GetTable(string path) where T1 : class, IFlatBufferArchive where T2 : class { var data = File.ReadAllBytes(path); + return GetTable(data); + } + + public static string GetTable(byte[] data) where T1 : class, IFlatBufferArchive where T2 : class + { var obj = FlatBufferConverter.DeserializeFrom(data); var table = obj.Table; return TableUtil.GetTable(table); diff --git a/pkNX.Structures.FlatBuffers/pkNX.Structures.FlatBuffers.csproj b/pkNX.Structures.FlatBuffers/pkNX.Structures.FlatBuffers.csproj index ee8126ca..9212767d 100644 --- a/pkNX.Structures.FlatBuffers/pkNX.Structures.FlatBuffers.csproj +++ b/pkNX.Structures.FlatBuffers/pkNX.Structures.FlatBuffers.csproj @@ -3,12 +3,14 @@ netstandard2.0;net461 Data Structures - With FlatBuffers! - 9 + 10 enable + + diff --git a/pkNX.Structures/Encounter/EncounterStatic.cs b/pkNX.Structures/Encounter/EncounterStatic.cs index 830ebe44..46a01107 100644 --- a/pkNX.Structures/Encounter/EncounterStatic.cs +++ b/pkNX.Structures/Encounter/EncounterStatic.cs @@ -86,7 +86,7 @@ public string GetSummary() str += $"Gender = {Gender - 1}, "; if (HeldItem > 0) str += $"HeldItem = {HeldItem}, "; - if (Nature >= 0 && Nature < Nature.Random25) + if (Nature is >= 0 and < Nature.Random25) str += $"Nature = {Nature - 1}, "; str += " },"; diff --git a/pkNX.Structures/Evolution/EvolutionSet7.cs b/pkNX.Structures/Evolution/EvolutionSet7.cs index d4aae2b9..a0182629 100644 --- a/pkNX.Structures/Evolution/EvolutionSet7.cs +++ b/pkNX.Structures/Evolution/EvolutionSet7.cs @@ -16,22 +16,19 @@ public EvolutionSet7(byte[] data) PossibleEvolutions = data.GetArray(GetEvo, ENTRY_SIZE); } - private static EvolutionMethod GetEvo(byte[] data, int offset) + private static EvolutionMethod GetEvo(byte[] data, int offset) => new() { - return new() - { - Method = (EvolutionType)BitConverter.ToUInt16(data, offset + 0), - Argument = BitConverter.ToUInt16(data, offset + 2), - Species = BitConverter.ToUInt16(data, offset + 4), - Form = (sbyte)data[offset + 6], - Level = data[offset + 7], - }; - } + Method = (EvolutionType)BitConverter.ToUInt16(data, offset + 0), + Argument = BitConverter.ToUInt16(data, offset + 2), + Species = BitConverter.ToUInt16(data, offset + 4), + Form = (sbyte)data[offset + 6], + Level = data[offset + 7], + }; public override byte[] Write() { - using MemoryStream ms = new MemoryStream(); - using BinaryWriter bw = new BinaryWriter(ms); + using MemoryStream ms = new(); + using BinaryWriter bw = new(ms); foreach (EvolutionMethod evo in PossibleEvolutions) { bw.Write((ushort)evo.Method); diff --git a/pkNX.Structures/Evolution/EvolutionSet8.cs b/pkNX.Structures/Evolution/EvolutionSet8.cs index 9594f53f..9904f3d8 100644 --- a/pkNX.Structures/Evolution/EvolutionSet8.cs +++ b/pkNX.Structures/Evolution/EvolutionSet8.cs @@ -16,22 +16,19 @@ public EvolutionSet8(byte[] data) PossibleEvolutions = data.GetArray(GetEvo, ENTRY_SIZE); } - private static EvolutionMethod GetEvo(byte[] data, int offset) + private static EvolutionMethod GetEvo(byte[] data, int offset) => new() { - return new() - { - Method = (EvolutionType)BitConverter.ToUInt16(data, offset + 0), - Argument = BitConverter.ToUInt16(data, offset + 2), - Species = BitConverter.ToUInt16(data, offset + 4), - Form = (sbyte)data[offset + 6], - Level = data[offset + 7], - }; - } + Method = (EvolutionType)BitConverter.ToUInt16(data, offset + 0), + Argument = BitConverter.ToUInt16(data, offset + 2), + Species = BitConverter.ToUInt16(data, offset + 4), + Form = (sbyte)data[offset + 6], + Level = data[offset + 7], + }; public override byte[] Write() { - using MemoryStream ms = new MemoryStream(); - using BinaryWriter bw = new BinaryWriter(ms); + using MemoryStream ms = new(); + using BinaryWriter bw = new(ms); foreach (EvolutionMethod evo in PossibleEvolutions) { bw.Write((ushort)evo.Method); diff --git a/pkNX.Structures/GameUtil.cs b/pkNX.Structures/GameUtil.cs index 2724d83b..8bbbc706 100644 --- a/pkNX.Structures/GameUtil.cs +++ b/pkNX.Structures/GameUtil.cs @@ -13,13 +13,13 @@ public static class GameUtil /// List of possible values that are stored in PKM data. /// /// Ordered roughly by most recent games first. - public static readonly GameVersion[] GameVersions = ((GameVersion[])Enum.GetValues(typeof(GameVersion))).Where(z => z < RB && z > 0).Reverse().ToArray(); + public static readonly GameVersion[] GameVersions = ((GameVersion[])Enum.GetValues(typeof(GameVersion))).Where(z => z is < RB and > 0).Reverse().ToArray(); /// /// Indicates if the value is a value used by the games or is an aggregate indicator. /// /// Game to check - public static bool IsValidSavedVersion(this GameVersion game) => 0 < game && game <= RB; + public static bool IsValidSavedVersion(this GameVersion game) => game is > 0 and <= RB; /// Determines the Version Grouping of an input Version ID /// Version of which to determine the group @@ -194,7 +194,7 @@ public static bool Contains(this GameVersion g1, GameVersion g2) Gen7b => GG.Contains(g2) || GO == g2, SWSH => g2 is SW or SH, - Gen8 => SWSH.Contains(g2), + Gen8 => SWSH.Contains(g2) || PLA == g2, _ => false, }; } diff --git a/pkNX.Structures/GameVersion.cs b/pkNX.Structures/GameVersion.cs index f87809a9..6d4073fd 100644 --- a/pkNX.Structures/GameVersion.cs +++ b/pkNX.Structures/GameVersion.cs @@ -202,6 +202,12 @@ public enum GameVersion /// Pokémon Shield (NX) /// SH = 45, + + PLA, + HOME, + BD, + SP, + #endregion // The following values are not actually stored values in pkm data, diff --git a/pkNX.Structures/Item/Item8a.cs b/pkNX.Structures/Item/Item8a.cs new file mode 100644 index 00000000..ed47ec8c --- /dev/null +++ b/pkNX.Structures/Item/Item8a.cs @@ -0,0 +1,194 @@ +using System; +using System.ComponentModel; + +namespace pkNX.Structures +{ + public class Item8a + { + private const int SIZE = 0x3C; + public readonly int ItemID; + public readonly byte[] Data; + + private const string Battle = "Battle"; + private const string Field = "Field"; + private const string Mart = "Mart"; + private const string Heal = "Heal"; + + public Item8a(int id, byte[] data) => (ItemID, Data) = (id, data); + + public uint Price { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); } + public uint PriceWatts { get => BitConverter.ToUInt32(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); } + public uint MeritPrice { get => BitConverter.ToUInt32(Data, 0x08); set => BitConverter.GetBytes(value).CopyTo(Data, 0x08); } + public byte BattleEffect { get => Data[0x0C]; set => Data[0x0C] = value; } + public byte BattleArg { get => Data[0x0D]; set => Data[0x0D] = value; } + public byte BerryValue { get => Data[0x0F]; set => Data[0x0F] = value; } + public byte Unk_0x10 { get => Data[0x10]; set => Data[0x10] = value; } + public PouchID8a Pouch + { + get => (PouchID8a)(Data[0x11] & 0xF); + set => Data[0x11] = (byte)((Data[0x11] & 0xF0) | ((byte)value & 0xF)); + } + + public ItemFlags8a Unknown + { + get => (ItemFlags8a)(Data[0x11] >> 4); + set => Data[0x11] = (byte)((Data[0x11] & 0x0F) | (((byte)value & 0xF) << 4)); + } + + public byte FlingPower { get => Data[0x12]; set => Data[0x12] = value; } + public FieldItemType Unk_0x13 { get => (FieldItemType)Data[0x13]; set => Data[0x13] = (byte)value; } + public BattlePouch8a BattlePouch { get => (BattlePouch8a)Data[0x14]; set => Data[0x14] = (byte)value; } + public bool CanUse { get => Data[0x15] != 0; set => Data[0x15] = (byte)(value ? 1 : 0); } + public ItemType8a ItemType { get => (ItemType8a)Data[0x16]; set => Data[0x16] = (byte)value; } + public byte Unk_0x17 { get => Data[0x17]; set => Data[0x17] = value; } + public byte SortIndex { get => Data[0x18]; set => Data[0x18] = value; } + // 0x19 align + public short ItemSprite { get => BitConverter.ToInt16(Data, 0x1A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1A); } + public ushort MaxQuantity { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); } + public ushort Percentage { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1E); } + public ItemClass8a ItemGroup { get => (ItemClass8a)Data[0x20]; set => Data[0x20] = (byte)value; } + public byte Variant { get => Data[0x21]; set => Data[0x21] = value; } + // 22 unused + // 23 unused + public BattleStatusFlags CureInflict { get => (BattleStatusFlags)Data[0x24]; set => Data[0x24] = (byte)value; } + public byte BallID { get => Data[0x24]; set => Data[0x24] = value; } // same offset as above + public byte Boost0 { get => Data[0x25]; set => Data[0x25] = value; } + public byte Boost1 { get => Data[0x26]; set => Data[0x26] = value; } + public byte Boost2 { get => Data[0x27]; set => Data[0x27] = value; } + public byte Boost3 { get => Data[0x28]; set => Data[0x28] = value; } + public ItemFlags1 FunctionFlags0 { get => (ItemFlags1)Data[0x29]; set => Data[0x29] = (byte)value; } + public ItemFlags2 FunctionFlags1 { get => (ItemFlags2)Data[0x2A]; set => Data[0x2A] = (byte)value; } + public sbyte EVHP { get => (sbyte)Data[0x2B]; set => Data[0x2B] = (byte)value; } + public sbyte EVATK { get => (sbyte)Data[0x2C]; set => Data[0x2C] = (byte)value; } + public sbyte EVDEF { get => (sbyte)Data[0x2D]; set => Data[0x2D] = (byte)value; } + public sbyte EVSPE { get => (sbyte)Data[0x2E]; set => Data[0x2E] = (byte)value; } + public sbyte EVSPA { get => (sbyte)Data[0x2F]; set => Data[0x2F] = (byte)value; } + public sbyte EVSPD { get => (sbyte)Data[0x30]; set => Data[0x30] = (byte)value; } + public Heal HealAmount { get => (Heal)Data[0x31]; set => Data[0x31] = (byte)value; } + public byte PPGain { get => Data[0x32]; set => Data[0x32] = value; } + public sbyte FriendshipGain1 { get => (sbyte)Data[0x33]; set => Data[0x33] = (byte)value; } + public sbyte FriendshipGain2 { get => (sbyte)Data[0x34]; set => Data[0x34] = (byte)value; } + public sbyte FriendshipGain3 { get => (sbyte)Data[0x35]; set => Data[0x35] = (byte)value; } + public byte Flags_36 { get => Data[0x36]; set => Data[0x36] = value; } + public byte EffectTurns1 { get => Data[0x37]; set => Data[0x37] = value; } // duration? + public byte EffectTurns2 { get => Data[0x38]; set => Data[0x38] = value; } // duration? + public byte EffectTurns3 { get => Data[0x39]; set => Data[0x39] = value; } // duration? + public sbyte StatChangeAmount { get => (sbyte)Data[0x3A]; set => Data[0x3A] = (byte)value; } + // 0x1B unused + + public static Item8a[] GetArray(byte[] bin) + { + int numEntries = BitConverter.ToUInt16(bin, 0); + int maxEntryIndex = BitConverter.ToUInt16(bin, 4); + int entriesStart = (int)BitConverter.ToUInt32(bin, 0x48); + var result = new Item8a[numEntries]; + for (var i = 0; i < result.Length; i++) + { + var entryIndex = BitConverter.ToUInt16(bin, 0x4C + (2 * i)); + if (entryIndex >= maxEntryIndex) { throw new ArgumentException(); } + result[i] = new Item8a(i, bin.Slice(entriesStart + (entryIndex * SIZE), SIZE)); + } + + return result; + } + + public static byte[] SetArray(Item8a[] array, byte[] bin) + { + bin = (byte[])bin.Clone(); + if (array.Length != BitConverter.ToInt16(bin, 0)) + throw new ArgumentException("Incompatible sizes"); + + int maxEntryIndex = BitConverter.ToUInt16(bin, 4); + int entriesStart = (int)BitConverter.ToUInt32(bin, 0x48); + for (int i = 0; i < array.Length; i++) + { + var entryIndex = BitConverter.ToUInt16(bin, 0x4C + (2 * i)); + if (entryIndex >= maxEntryIndex) { throw new ArgumentException(); } + + var data = array[i].Data; + data.CopyTo(bin, entriesStart + (entryIndex * SIZE)); + } + + return bin; + } + + [Category(Field)] public bool Revive { get => ((Boost0 >> 0) & 1) == 0; set => Boost0 = (byte)((Boost0 & ~(1 << 0)) | ((value ? 1 : 0) << 0)); } + [Category(Field)] public bool ReviveAll { get => ((Boost0 >> 1) & 1) == 1; set => Boost0 = (byte)((Boost0 & ~(1 << 1)) | ((value ? 1 : 0) << 1)); } + [Category(Field)] public bool LevelUp { get => ((Boost0 >> 2) & 1) == 1; set => Boost0 = (byte)((Boost0 & ~(1 << 2)) | ((value ? 1 : 0) << 2)); } + [Category(Field)] public bool EvoStone { get => ((Boost0 >> 3) & 1) == 1; set => Boost0 = (byte)((Boost0 & ~(1 << 3)) | ((value ? 1 : 0) << 3)); } + [Category(Battle)] public int BoostATK { get => Boost0 >> 4; set => Boost0 = (byte)((Boost0 & 0xF) | (value << 4)); } + [Category(Battle)] public int BoostDEF { get => Boost1 & 0xF; set => Boost1 = (byte)((Boost1 & ~0xF) | (value & 0xF)); } + [Category(Battle)] public int BoostSPA { get => Boost1 >> 4; set => Boost1 = (byte)((Boost1 & 0xF) | (value << 4)); } + [Category(Battle)] public int BoostSPD { get => Boost2 & 0xF; set => Boost2 = (byte)((Boost2 & ~0xF) | (value & 0xF)); } + [Category(Battle)] public int BoostSPE { get => Boost2 >> 4; set => Boost2 = (byte)((Boost2 & 0xF) | (value << 4)); } + [Category(Battle)] public int BoostACC { get => Boost3 & 0xF; set => Boost3 = (byte)((Boost3 & ~0xF) | (value & 0xF)); } + [Category(Battle)] public int BoostCRIT { get => (Boost3 >> 4) & 3; set => Boost3 = (byte)((Boost3 & ~0x30) | ((value & 3) << 4)); } + [Category(Battle)] public int BoostPP1 { get => (Boost3 >> 6) & 1; set => Boost3 = (byte)((Boost3 & 0xBF) | ((value & 1) << 6)); } + [Category(Battle)] public int BoostPPMax { get => (Boost3 >> 7) & 1; set => Boost3 = (byte)((Boost3 & 0x7F) | ((value & 1) << 7)); } + } + + public enum ItemClass8a + { + None, + Ball = 1, + Berry = 3, + TM = 4, + Gem = 6, + Charm = 14, + Plate = 15, + } + + public enum ItemType8a : byte + { + Pocket = 0, + Medicinal = 1, + Equip = 2, + Treasure = 3, + BattleItem = 4, + Ball = 5, + Mail = 6, + TM = 7, + Berry = 8, + Key = 9, + LegendItemUseBattle = 10, + LegendItemToss = 11, + Rare = 12, + Dummy = 255, + } + + public enum BattlePouch8a : byte + { + None = 0, + Balls = 1, + Use = 2, + } + + public enum PouchID8a : byte + { + Regular, + Key, + Recipe, + } + + [Flags] + public enum ItemFlags8a : byte + { + None = 0, + Flag1 = 1, + Flag2 = 2, + Flag4 = 4, + Flag8 = 8, + } + + public enum FieldItemType : byte + { + Inert, + Medicine = 1, + TM = 2, + Spray = 5, + Evolution = 6, + EscapeRope = 7, + Berry = 12, + FormChange = 15, + } +} diff --git a/pkNX.Structures/Legality/GameInfo.cs b/pkNX.Structures/Legality/GameInfo.cs index 62a0bed8..fcddcfa9 100644 --- a/pkNX.Structures/Legality/GameInfo.cs +++ b/pkNX.Structures/Legality/GameInfo.cs @@ -46,6 +46,7 @@ private Action GetInitMethod(GameVersion game) GameVersion.SW => LoadSWSH, GameVersion.SH => LoadSWSH, GameVersion.SWSH => LoadSWSH, + GameVersion.PLA => LoadPLA, _ => throw new ArgumentException(nameof(game)) }; } @@ -109,5 +110,15 @@ private void LoadSWSH() HeldItems = Legal.HeldItems_SWSH; MaxAbilityID = Legal.MaxAbilityID_8; } + + private void LoadPLA() + { + SWSH = true; + MaxSpeciesID = Legal.MaxSpeciesID_8a; + MaxMoveID = Legal.MaxMoveID_8a; + MaxItemID = Legal.MaxItemID_8a; + HeldItems = Legal.HeldItems_SWSH; + MaxAbilityID = Legal.MaxAbilityID_8a; + } } } diff --git a/pkNX.Structures/Legality/Legal.cs b/pkNX.Structures/Legality/Legal.cs index 959b9a52..e9e1f81f 100644 --- a/pkNX.Structures/Legality/Legal.cs +++ b/pkNX.Structures/Legality/Legal.cs @@ -15,11 +15,7 @@ public static partial class Legal public static int GetModifiedLevel(int level, double factor) { int newlvl = (int)(level * factor); - if (newlvl < 1) - return 1; - if (newlvl > 100) - return 100; - return newlvl; + return Math.Max(1, Math.Min(newlvl, 100)); } public static int[] GetRandomItemList(GameVersion game) diff --git a/pkNX.Structures/Legality/Tables6.cs b/pkNX.Structures/Legality/Tables6.cs index 81abb455..f6f06898 100644 --- a/pkNX.Structures/Legality/Tables6.cs +++ b/pkNX.Structures/Legality/Tables6.cs @@ -16,7 +16,7 @@ public static partial class Legal public const int MaxGameID_6 = 27; // OR #region Met Locations - public static readonly int[] Met_XYc = {0, 60002, 30002,}; + public static readonly int[] Met_XYc = {0, 60002, 30002}; public static readonly int[] Met_XY_0 = { @@ -46,7 +46,7 @@ public static partial class Legal 40071, 40072, 40073, 40074, 40075, 40076, 40077, 40078, 40079, }; - public static readonly int[] Met_XY_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004,}; + public static readonly int[] Met_XY_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004}; #endregion diff --git a/pkNX.Structures/Legality/Tables7b.cs b/pkNX.Structures/Legality/Tables7b.cs index 3dfe6618..d075dfb8 100644 --- a/pkNX.Structures/Legality/Tables7b.cs +++ b/pkNX.Structures/Legality/Tables7b.cs @@ -41,7 +41,7 @@ public static partial class Legal 40070, 40071, 40072, 40073, 40074, 40075, 40076, 40077, }; - internal static readonly int[] Met_GG_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004, }; + internal static readonly int[] Met_GG_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004}; #endregion diff --git a/pkNX.Structures/Legality/Tables8.cs b/pkNX.Structures/Legality/Tables8.cs index 6eac1aeb..040d2987 100644 --- a/pkNX.Structures/Legality/Tables8.cs +++ b/pkNX.Structures/Legality/Tables8.cs @@ -12,6 +12,13 @@ public static partial class Legal public const int MaxBallID_8 = (int)Ball.Beast; public const int MaxGameID_8 = (int)GameVersion.SH; + public const int MaxSpeciesID_8a = 898; // Calyrex + public const int MaxMoveID_8a = 826; // Eerie Spell + public const int MaxItemID_8a = 1607; // Reins of Unity + public const int MaxAbilityID_8a = 267; // As One + public const int MaxBallID_8a = (int)Ball.Beast; + public const int MaxGameID_8a = (int)GameVersion.SH; + #region Met Locations internal static readonly int[] Met_SWSH_0 = { @@ -62,7 +69,7 @@ public static partial class Legal 40081, 40082, 40083, 40084, 40085, 40086, }; - internal static readonly int[] Met_SWSH_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004, }; + internal static readonly int[] Met_SWSH_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004}; #endregion internal static readonly ushort[] Pouch_Regular_SWSH = @@ -197,6 +204,18 @@ public static partial class Legal 798, 802, }; + public static readonly int[] MoveShop8a = + { + 0xCE, 0x1A8, 0x1A6, 0x1A7, 0x12D, 0xF9, 0xBF, 0x20B, + 0x14C, 0x1BE, 0x81, 0xA1, 0x159, 0x1D2, 0x33D, 0x74, + 0x153, 0x15B, 0x9C, 0x260, 7, 9, 8, 0x199, 0x18E, 0x1AB, + 0x1AC, 0x8D, 0x194, 0x9D, 0x1A5, 0x1BA, 0xE7, 0x253, + 0x160, 0x1C3, 0x19C, 0xC4, 0xBC, 0x19E, 0xF7, 0x22B, + 0x1AE, 0x25D, 0x1A0, 0x191, 0x210, 0x29B, 0xE0, 0x1BC, + 0xC8, 0x247, 0x3F, 0x35, 0x55, 0x3A, 0x5E, 0x18F, 0x1B2, + 0x31C, 0x158, + }; + internal static readonly HashSet GalarOriginForms = new() { (int)Species.Meowth, diff --git a/pkNX.Structures/Misc/CaptureReward.cs b/pkNX.Structures/Misc/CaptureReward.cs index 89a33dcf..e2f0b9bc 100644 --- a/pkNX.Structures/Misc/CaptureReward.cs +++ b/pkNX.Structures/Misc/CaptureReward.cs @@ -31,22 +31,19 @@ private static void ReadTable(BinaryReader br, CaptureRewardGroup g) g.Entries.Add(ReadEntry(br)); } - private static CaptureRewardEntry ReadEntry(BinaryReader br) + private static CaptureRewardEntry ReadEntry(BinaryReader br) => new() { - return new() - { - Item = br.ReadInt32(), - Count = br.ReadInt32(), - Rate = br.ReadInt32(), - }; - } + Item = br.ReadInt32(), + Count = br.ReadInt32(), + Rate = br.ReadInt32(), + }; private void ReadHeader(BinaryReader br) { while (true) { var count = br.ReadInt32(); - if (Table.Count != 0 && Table[Table.Count - 1].CaptureCount > count) + if (Table.Count != 0 && Table[^1].CaptureCount > count) { br.BaseStream.Position -= 4; break; diff --git a/pkNX.Structures/Move/MoveInflictDuration.cs b/pkNX.Structures/Move/MoveInflictDuration.cs index 849062b3..316e2c98 100644 --- a/pkNX.Structures/Move/MoveInflictDuration.cs +++ b/pkNX.Structures/Move/MoveInflictDuration.cs @@ -1,11 +1,10 @@ -namespace pkNX.Structures +namespace pkNX.Structures; + +public enum MoveInflictDuration { - public enum MoveInflictDuration - { - None = 0, - Permanent, - TurnCountSwitch, - PermanentSwitch, - TurnCountNoSwitch, - }; -} \ No newline at end of file + None = 0, + Permanent, + TurnCountSwitch, + PermanentSwitch, + TurnCountNoSwitch, +} diff --git a/pkNX.Structures/Personal/PersonalInfo.cs b/pkNX.Structures/Personal/PersonalInfo.cs index 3575c7eb..0263acc7 100644 --- a/pkNX.Structures/Personal/PersonalInfo.cs +++ b/pkNX.Structures/Personal/PersonalInfo.cs @@ -7,7 +7,7 @@ namespace pkNX.Structures /// public abstract class PersonalInfo { - protected byte[] Data; + public byte[] Data; public abstract byte[] Write(); public abstract int HP { get; set; } public abstract int ATK { get; set; } diff --git a/pkNX.Structures/Personal/PersonalInfoLA.cs b/pkNX.Structures/Personal/PersonalInfoLA.cs new file mode 100644 index 00000000..137a7cd0 --- /dev/null +++ b/pkNX.Structures/Personal/PersonalInfoLA.cs @@ -0,0 +1,147 @@ +using System; + +namespace pkNX.Structures +{ + /// + /// class with values from the games. + /// + public sealed class PersonalInfoLA : PersonalInfo + { + public const int SIZE = 0xB0; + + public PersonalInfoLA(byte[] data) + { + Data = data; + LoadTMHM(); + LoadTutors(); + LoadSpecial(); + } + + public void LoadSpecial() + { + // 0xA8-0xAF are armor type tutors, one bit for each type + var armorTutors = new bool[18]; + for (int i = 0; i < armorTutors.Length; i++) + armorTutors[i] = FlagUtil.GetFlag(Data, 0xA8 + (i >> 3), i); + SpecialTutors = new[] + { + armorTutors, + }; + } + + public void LoadTutors() + { + TypeTutors = Array.Empty(); + + // 0x38-0x3B type tutors, but only 8 bits are valid flags. + var typeTutors = new bool[8]; + for (int i = 0; i < typeTutors.Length; i++) + typeTutors[i] = FlagUtil.GetFlag(Data, 0x38, i); + TypeTutors = typeTutors; + } + + public void LoadTMHM() + { + TMHM = new bool[200]; + for (var i = 0; i < 100; i++) + { + TMHM[i] = FlagUtil.GetFlag(Data, 0x28 + (i >> 3), i); + TMHM[i + 100] = FlagUtil.GetFlag(Data, 0x3C + (i >> 3), i); + } + } + + public override byte[] Write() + { + for (var i = 0; i < 100; i++) + { + FlagUtil.SetFlag(Data, 0x28 + (i >> 3), i, TMHM[i]); + FlagUtil.SetFlag(Data, 0x3C + (i >> 3), i, TMHM[i + 100]); + } + for (int i = 0; i < TypeTutors.Length; i++) + FlagUtil.SetFlag(Data, 0x38, i, TypeTutors[i]); + for (int i = 0; i < SpecialTutors[0].Length; i++) + FlagUtil.SetFlag(Data, 0xA8 + (i >> 3), i, SpecialTutors[0][i]); + return Data; + } + + public override int HP { get => Data[0x00]; set => Data[0x00] = (byte)value; } + public override int ATK { get => Data[0x01]; set => Data[0x01] = (byte)value; } + public override int DEF { get => Data[0x02]; set => Data[0x02] = (byte)value; } + public override int SPE { get => Data[0x03]; set => Data[0x03] = (byte)value; } + public override int SPA { get => Data[0x04]; set => Data[0x04] = (byte)value; } + public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; } + public override int Type1 { get => Data[0x06]; set => Data[0x06] = (byte)value; } + public override int Type2 { get => Data[0x07]; set => Data[0x07] = (byte)value; } + public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; } + public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; } + private int EVYield { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); } + public override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; } + public override int EV_ATK { get => EVYield >> 2 & 0x3; set => EVYield = (EVYield & ~(0x3 << 2)) | (value & 0x3) << 2; } + public override int EV_DEF { get => EVYield >> 4 & 0x3; set => EVYield = (EVYield & ~(0x3 << 4)) | (value & 0x3) << 4; } + public override int EV_SPE { get => EVYield >> 6 & 0x3; set => EVYield = (EVYield & ~(0x3 << 6)) | (value & 0x3) << 6; } + public override int EV_SPA { get => EVYield >> 8 & 0x3; set => EVYield = (EVYield & ~(0x3 << 8)) | (value & 0x3) << 8; } + public override int EV_SPD { get => EVYield >> 10 & 0x3; set => EVYield = (EVYield & ~(0x3 << 10)) | (value & 0x3) << 10; } + public int Item1 { get => BitConverter.ToInt16(Data, 0x0C); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0C); } + public int Item2 { get => BitConverter.ToInt16(Data, 0x0E); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0E); } + public int Item3 { get => BitConverter.ToInt16(Data, 0x10); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x10); } + public override int Gender { get => Data[0x12]; set => Data[0x12] = (byte)value; } + public override int HatchCycles { get => Data[0x13]; set => Data[0x13] = (byte)value; } + public override int BaseFriendship { get => Data[0x14]; set => Data[0x14] = (byte)value; } + public override int EXPGrowth { get => Data[0x15]; set => Data[0x15] = (byte)value; } + public override int EggGroup1 { get => Data[0x16]; set => Data[0x16] = (byte)value; } + public override int EggGroup2 { get => Data[0x17]; set => Data[0x17] = (byte)value; } + public int Ability1 { get => BitConverter.ToUInt16(Data, 0x18); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); } + public int Ability2 { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1A); } + public int AbilityH { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); } + public override int EscapeRate { get => 0; set { } } // moved? + public void SetFormStat(int index) => FormStatsIndex = index; + protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } + public override int FormeSprite { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); } // ??? + public override int FormeCount { get => Data[0x20]; set => Data[0x20] = (byte)value; } + public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); } + public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); } + public bool SpriteForme { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); } + public override int BaseEXP { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } + public override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); } + public override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); } + + public override int[] Items + { + get => new[] { Item1, Item2, Item3 }; + set + { + if (value?.Length != 3) return; + Item1 = value[0]; + Item2 = value[1]; + Item3 = value[2]; + } + } + + public override int[] Abilities + { + get => new[] { Ability1, Ability2, AbilityH }; + set + { + if (value?.Length != 3) return; + Ability1 = value[0]; + Ability2 = value[1]; + AbilityH = value[2]; + } + } + + public int SpriteIndex { get => BitConverter.ToUInt16(Data, 0x4C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4C); } + public int HatchSpecies { get => BitConverter.ToUInt16(Data, 0x56); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x56); } + public int LocalFormIndex { get => BitConverter.ToUInt16(Data, 0x58); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x58); } // local region base form + public ushort RegionalFlags { get => BitConverter.ToUInt16(Data, 0x5A); set => BitConverter.GetBytes(value).CopyTo(Data, 0x5A); } + public bool IsRegionalForm { get => (RegionalFlags & 1) == 1; set => BitConverter.GetBytes((ushort)((RegionalFlags & 0xFFFE) | (value ? 1 : 0))).CopyTo(Data, 0x5A); } + public bool CanNotDynamax { get => ((Data[0x5A] >> 2) & 1) == 1; set => Data[0x5A] = (byte)((Data[0x5A] & ~4) | (value ? 4 : 0)); } + public int Species { get => BitConverter.ToUInt16(Data, 0x5C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5C); } + public int Form { get => BitConverter.ToUInt16(Data, 0x5E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x5E); } + public int DexIndexHisui { get => BitConverter.ToUInt16(Data, 0x60); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x60); } + public int DexIndexLocal1 { get => BitConverter.ToUInt16(Data, 0x62); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x62); } + public int DexIndexLocal2 { get => BitConverter.ToUInt16(Data, 0x64); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x64); } + public int DexIndexLocal3 { get => BitConverter.ToUInt16(Data, 0x66); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x66); } + public int DexIndexLocal4 { get => BitConverter.ToUInt16(Data, 0x68); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x68); } + public int DexIndexLocal5 { get => BitConverter.ToUInt16(Data, 0x6A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); } + } +} diff --git a/pkNX.Structures/Personal/PersonalTable.cs b/pkNX.Structures/Personal/PersonalTable.cs index 4fa7fc3e..f263e602 100644 --- a/pkNX.Structures/Personal/PersonalTable.cs +++ b/pkNX.Structures/Personal/PersonalTable.cs @@ -46,6 +46,7 @@ private static int GetEntrySize(GameVersion format) GameVersion.ORAS => PersonalInfoORAS.SIZE, GameVersion.SM or GameVersion.USUM or GameVersion.GP or GameVersion.GE or GameVersion.GG => PersonalInfoSM.SIZE, GameVersion.SW or GameVersion.SH or GameVersion.SWSH => PersonalInfoSWSH.SIZE, + GameVersion.PLA => PersonalInfoLA.SIZE, _ => -1 }; } @@ -62,6 +63,12 @@ public PersonalTable(byte[] data, GameVersion format) MaxSpeciesID = format.GetMaxSpeciesID(); } + public PersonalTable(PersonalInfo[] table, int max) + { + Table = table; + MaxSpeciesID = max; + } + public readonly PersonalInfo[] Table; /// diff --git a/pkNX.Structures/Scripts/Amx.cs b/pkNX.Structures/Scripts/Amx.cs index 139f0a3a..435723f6 100644 --- a/pkNX.Structures/Scripts/Amx.cs +++ b/pkNX.Structures/Scripts/Amx.cs @@ -70,8 +70,8 @@ public IEnumerable SummaryLines } public Function LookupFunction(uint pc) => Array.Find(Functions, f => f.Within(pc)); - public TableRecord LookupPublic(string name) => Array.Find(this.Publics, t => t.Name == name); - public TableRecord LookupPublic(uint addr) => Array.Find(this.Publics, t => t.Address == addr); + public TableRecord LookupPublic(string name) => Array.Find(Publics, t => t.Name == name); + public TableRecord LookupPublic(uint addr) => Array.Find(Publics, t => t.Address == addr); public Function[] Functions { get; protected set; } public TableRecord[] Publics { get; protected set; } diff --git a/pkNX.Structures/Scripts/CellType.cs b/pkNX.Structures/Scripts/CellType.cs index 7b578239..a8fc6f35 100644 --- a/pkNX.Structures/Scripts/CellType.cs +++ b/pkNX.Structures/Scripts/CellType.cs @@ -1,12 +1,11 @@ -namespace pkNX.Structures +namespace pkNX.Structures; + +public enum CellType { - public enum CellType - { - None, - Bool, - Float, - Character, - Tag, - Function - }; -} \ No newline at end of file + None, + Bool, + Float, + Character, + Tag, + Function, +} diff --git a/pkNX.Structures/Scripts/PawnUtil.cs b/pkNX.Structures/Scripts/PawnUtil.cs index 85e4e893..3162974d 100644 --- a/pkNX.Structures/Scripts/PawnUtil.cs +++ b/pkNX.Structures/Scripts/PawnUtil.cs @@ -141,7 +141,7 @@ private static byte[] CompressInstruction(uint instruction) public static string[] ParseScript(uint[] cmd, int sanity = -1) { // sub_148CBC Moon v1.0 - List parse = new List(); + List parse = new(); const int sanityMode = 0; // todo string ErrorNear(int line, string error) @@ -288,7 +288,7 @@ string ErrorNear(int line, string error) } } - if (opcode == AmxOpCode.RET || opcode == AmxOpCode.RETN || opcode == AmxOpCode.IRETN) + if (opcode is AmxOpCode.RET or AmxOpCode.RETN or AmxOpCode.IRETN) { // Newline after return instrLine += Environment.NewLine; @@ -315,7 +315,7 @@ internal static string EchoIntCommand(AmxOpCode c, params int[] arr) { static string FormatParameter(int param) { - if (param < -100 || param > 100) + if (param is < -100 or > 100) return $"0x{param:X4}"; return param.ToString(); } diff --git a/pkNX.Structures/TableUtil.cs b/pkNX.Structures/TableUtil.cs index 26e9dc18..4759e873 100644 --- a/pkNX.Structures/TableUtil.cs +++ b/pkNX.Structures/TableUtil.cs @@ -90,7 +90,16 @@ private static string GetFormattedString(object obj) return u.ToString("X16"); if (obj is IEnumerable x and not string) return string.Join("|", JoinEnumerator(x.GetEnumerator()).Select(GetFormattedString)); - return obj.ToString(); + + var objType = obj.GetType(); + if (objType.IsEnum) + return obj.ToString(); + var mi = objType.GetMethods().First(z => z.Name == nameof(obj.ToString)); + if (mi.DeclaringType == objType) + return obj.ToString(); + + var props = objType.GetProperties(); + return string.Join("|", props.Select(z => GetFormattedString(z.GetValue(obj)))); } private static IEnumerable JoinEnumerator(IEnumerator x) diff --git a/pkNX.Structures/Text/TextFile.cs b/pkNX.Structures/Text/TextFile.cs index eea240b0..891891ed 100644 --- a/pkNX.Structures/Text/TextFile.cs +++ b/pkNX.Structures/Text/TextFile.cs @@ -192,7 +192,7 @@ private byte[] GetLineData(string line) int bracket = line.IndexOf(']', i); if (bracket < 0) throw new ArgumentException("Variable text is not capped properly: " + line); - string varText = line.Substring(i, bracket - i); + string varText = line[i..bracket]; var varValues = GetVariableValues(varText); foreach (ushort v in varValues) bw.Write(v); i += 1 + varText.Length; @@ -257,12 +257,12 @@ private string GetLineString(byte[] data) while (i < data.Length) { ushort val = BitConverter.ToUInt16(data, i); - if (val == KEY_TERMINATOR) break; + if (val == KEY_TERMINATOR) + break; i += 2; switch (val) { - case KEY_TERMINATOR: return s.ToString(); case KEY_VARIABLE: s.Append(GetVariableString(Config, data, ref i)); break; case '\n': s.Append(@"\n"); break; case '\\': s.Append(@"\\"); break; @@ -358,7 +358,7 @@ private IEnumerable GetVariableParameters(string text) var vals = new List(); int bracket = text.IndexOf('('); bool noArgs = bracket < 0; - string variable = noArgs ? text : text.Substring(0, bracket); + string variable = noArgs ? text : text[..bracket]; ushort varVal = Config.GetVariableNumber(variable); if (!noArgs) diff --git a/pkNX.Structures/pkNX.Structures.csproj b/pkNX.Structures/pkNX.Structures.csproj index 7c104953..f4ab5e75 100644 --- a/pkNX.Structures/pkNX.Structures.csproj +++ b/pkNX.Structures/pkNX.Structures.csproj @@ -3,7 +3,16 @@ netstandard2.0;net461 Data Structures - 9 + 10 + + + + + + + + + diff --git a/pkNX.Tests/pkNX.Tests.csproj b/pkNX.Tests/pkNX.Tests.csproj index 3067b7d3..d4c793ad 100644 --- a/pkNX.Tests/pkNX.Tests.csproj +++ b/pkNX.Tests/pkNX.Tests.csproj @@ -1,8 +1,8 @@  - net5.0 - 9 + net6.0 + 10 enable false @@ -12,7 +12,9 @@ + + all diff --git a/pkNX.WinForms/Dumping/DumperPLA.Designer.cs b/pkNX.WinForms/Dumping/DumperPLA.Designer.cs new file mode 100644 index 00000000..bf657ed3 --- /dev/null +++ b/pkNX.WinForms/Dumping/DumperPLA.Designer.cs @@ -0,0 +1,459 @@ +namespace pkNX.WinForms +{ + partial class DumperPLA + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.TC_Options = new System.Windows.Forms.TabControl(); + this.Tab_General = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.B_ParsePKMDetails = new System.Windows.Forms.Button(); + this.B_DumpTrainers = new System.Windows.Forms.Button(); + this.B_Wild = new System.Windows.Forms.Button(); + this.B_Static = new System.Windows.Forms.Button(); + this.B_Gift = new System.Windows.Forms.Button(); + this.B_ItemInfo = new System.Windows.Forms.Button(); + this.B_Moves = new System.Windows.Forms.Button(); + this.B_Details = new System.Windows.Forms.Button(); + this.B_Placement = new System.Windows.Forms.Button(); + this.B_Resident = new System.Windows.Forms.Button(); + this.B_DumpDrops = new System.Windows.Forms.Button(); + this.Tab_PKHeX = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.B_PKText = new System.Windows.Forms.Button(); + this.B_PKLearn = new System.Windows.Forms.Button(); + this.B_PKEvo = new System.Windows.Forms.Button(); + this.Tab_Misc = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); + this.B_DumpHash = new System.Windows.Forms.Button(); + this.B_FlavorText = new System.Windows.Forms.Button(); + this.B_EggMove = new System.Windows.Forms.Button(); + this.B_Dex = new System.Windows.Forms.Button(); + this.B_Outbreak = new System.Windows.Forms.Button(); + this.B_MoveShop = new System.Windows.Forms.Button(); + this.B_ScriptCommandNames = new System.Windows.Forms.Button(); + this.Tab_Future = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); + this.B_OpenFolder = new System.Windows.Forms.Button(); + this.TC_Options.SuspendLayout(); + this.Tab_General.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.Tab_PKHeX.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + this.Tab_Misc.SuspendLayout(); + this.flowLayoutPanel3.SuspendLayout(); + this.Tab_Future.SuspendLayout(); + this.SuspendLayout(); + // + // TC_Options + // + this.TC_Options.Controls.Add(this.Tab_General); + this.TC_Options.Controls.Add(this.Tab_PKHeX); + this.TC_Options.Controls.Add(this.Tab_Misc); + this.TC_Options.Controls.Add(this.Tab_Future); + this.TC_Options.Dock = System.Windows.Forms.DockStyle.Fill; + this.TC_Options.Location = new System.Drawing.Point(0, 0); + this.TC_Options.Name = "TC_Options"; + this.TC_Options.SelectedIndex = 0; + this.TC_Options.Size = new System.Drawing.Size(401, 279); + this.TC_Options.TabIndex = 0; + // + // Tab_General + // + this.Tab_General.Controls.Add(this.flowLayoutPanel1); + this.Tab_General.Location = new System.Drawing.Point(4, 22); + this.Tab_General.Name = "Tab_General"; + this.Tab_General.Padding = new System.Windows.Forms.Padding(3); + this.Tab_General.Size = new System.Drawing.Size(393, 253); + this.Tab_General.TabIndex = 0; + this.Tab_General.Text = "General"; + this.Tab_General.UseVisualStyleBackColor = true; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Controls.Add(this.B_ParsePKMDetails); + this.flowLayoutPanel1.Controls.Add(this.B_DumpTrainers); + this.flowLayoutPanel1.Controls.Add(this.B_Wild); + this.flowLayoutPanel1.Controls.Add(this.B_Static); + this.flowLayoutPanel1.Controls.Add(this.B_Gift); + this.flowLayoutPanel1.Controls.Add(this.B_ItemInfo); + this.flowLayoutPanel1.Controls.Add(this.B_Moves); + this.flowLayoutPanel1.Controls.Add(this.B_Details); + this.flowLayoutPanel1.Controls.Add(this.B_Placement); + this.flowLayoutPanel1.Controls.Add(this.B_Resident); + this.flowLayoutPanel1.Controls.Add(this.B_DumpDrops); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(387, 247); + this.flowLayoutPanel1.TabIndex = 1; + // + // B_ParsePKMDetails + // + this.B_ParsePKMDetails.Location = new System.Drawing.Point(3, 3); + this.B_ParsePKMDetails.Name = "B_ParsePKMDetails"; + this.B_ParsePKMDetails.Size = new System.Drawing.Size(123, 55); + this.B_ParsePKMDetails.TabIndex = 1; + this.B_ParsePKMDetails.Text = "Personal Stats"; + this.B_ParsePKMDetails.UseVisualStyleBackColor = true; + this.B_ParsePKMDetails.Click += new System.EventHandler(this.B_ParsePersonal_Click); + // + // B_DumpTrainers + // + this.B_DumpTrainers.Location = new System.Drawing.Point(132, 3); + this.B_DumpTrainers.Name = "B_DumpTrainers"; + this.B_DumpTrainers.Size = new System.Drawing.Size(123, 55); + this.B_DumpTrainers.TabIndex = 2; + this.B_DumpTrainers.Text = "Trainers"; + this.B_DumpTrainers.UseVisualStyleBackColor = true; + this.B_DumpTrainers.Click += new System.EventHandler(this.B_DumpTrainers_Click); + // + // B_Wild + // + this.B_Wild.Location = new System.Drawing.Point(261, 3); + this.B_Wild.Name = "B_Wild"; + this.B_Wild.Size = new System.Drawing.Size(123, 55); + this.B_Wild.TabIndex = 11; + this.B_Wild.Text = "Wild Encounters"; + this.B_Wild.UseVisualStyleBackColor = true; + this.B_Wild.Click += new System.EventHandler(this.B_Wild_Click); + // + // B_Static + // + this.B_Static.Location = new System.Drawing.Point(3, 64); + this.B_Static.Name = "B_Static"; + this.B_Static.Size = new System.Drawing.Size(123, 55); + this.B_Static.TabIndex = 10; + this.B_Static.Text = "Static Encounters"; + this.B_Static.UseVisualStyleBackColor = true; + this.B_Static.Click += new System.EventHandler(this.B_Static_Click); + // + // B_Gift + // + this.B_Gift.Location = new System.Drawing.Point(132, 64); + this.B_Gift.Name = "B_Gift"; + this.B_Gift.Size = new System.Drawing.Size(123, 55); + this.B_Gift.TabIndex = 9; + this.B_Gift.Text = "Gift Encounters"; + this.B_Gift.UseVisualStyleBackColor = true; + this.B_Gift.Click += new System.EventHandler(this.B_Gift_Click); + // + // B_ItemInfo + // + this.B_ItemInfo.Location = new System.Drawing.Point(261, 64); + this.B_ItemInfo.Name = "B_ItemInfo"; + this.B_ItemInfo.Size = new System.Drawing.Size(123, 55); + this.B_ItemInfo.TabIndex = 6; + this.B_ItemInfo.Text = "Item Info"; + this.B_ItemInfo.UseVisualStyleBackColor = true; + this.B_ItemInfo.Click += new System.EventHandler(this.B_ItemInfo_Click); + // + // B_Moves + // + this.B_Moves.Location = new System.Drawing.Point(3, 125); + this.B_Moves.Name = "B_Moves"; + this.B_Moves.Size = new System.Drawing.Size(123, 55); + this.B_Moves.TabIndex = 7; + this.B_Moves.Text = "Move Info"; + this.B_Moves.UseVisualStyleBackColor = true; + this.B_Moves.Click += new System.EventHandler(this.B_Moves_Click); + // + // B_Details + // + this.B_Details.Location = new System.Drawing.Point(132, 125); + this.B_Details.Name = "B_Details"; + this.B_Details.Size = new System.Drawing.Size(123, 55); + this.B_Details.TabIndex = 15; + this.B_Details.Text = "Poke Details"; + this.B_Details.UseVisualStyleBackColor = true; + this.B_Details.Click += new System.EventHandler(this.B_ParsePKMDetails_Click); + // + // B_Placement + // + this.B_Placement.Location = new System.Drawing.Point(261, 125); + this.B_Placement.Name = "B_Placement"; + this.B_Placement.Size = new System.Drawing.Size(123, 55); + this.B_Placement.TabIndex = 18; + this.B_Placement.Text = "Placement"; + this.B_Placement.UseVisualStyleBackColor = true; + this.B_Placement.Click += new System.EventHandler(this.B_Placement_Click); + // + // B_Resident + // + this.B_Resident.Location = new System.Drawing.Point(3, 186); + this.B_Resident.Name = "B_Resident"; + this.B_Resident.Size = new System.Drawing.Size(123, 55); + this.B_Resident.TabIndex = 19; + this.B_Resident.Text = "Resident"; + this.B_Resident.UseVisualStyleBackColor = true; + this.B_Resident.Click += new System.EventHandler(this.B_Resident_Click); + // + // B_DumpDrops + // + this.B_DumpDrops.Location = new System.Drawing.Point(132, 186); + this.B_DumpDrops.Name = "B_DumpDrops"; + this.B_DumpDrops.Size = new System.Drawing.Size(123, 55); + this.B_DumpDrops.TabIndex = 20; + this.B_DumpDrops.Text = "PokeDrops"; + this.B_DumpDrops.UseVisualStyleBackColor = true; + this.B_DumpDrops.Click += new System.EventHandler(this.B_PokeDrops_Click); + // + // Tab_PKHeX + // + this.Tab_PKHeX.Controls.Add(this.flowLayoutPanel2); + this.Tab_PKHeX.Location = new System.Drawing.Point(4, 22); + this.Tab_PKHeX.Name = "Tab_PKHeX"; + this.Tab_PKHeX.Padding = new System.Windows.Forms.Padding(3); + this.Tab_PKHeX.Size = new System.Drawing.Size(393, 253); + this.Tab_PKHeX.TabIndex = 1; + this.Tab_PKHeX.Text = "PKHeX"; + this.Tab_PKHeX.UseVisualStyleBackColor = true; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Controls.Add(this.B_PKText); + this.flowLayoutPanel2.Controls.Add(this.B_PKLearn); + this.flowLayoutPanel2.Controls.Add(this.B_PKEvo); + this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel2.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(387, 247); + this.flowLayoutPanel2.TabIndex = 0; + // + // B_PKText + // + this.B_PKText.Location = new System.Drawing.Point(3, 3); + this.B_PKText.Name = "B_PKText"; + this.B_PKText.Size = new System.Drawing.Size(123, 55); + this.B_PKText.TabIndex = 5; + this.B_PKText.Text = "Text Files"; + this.B_PKText.UseVisualStyleBackColor = true; + this.B_PKText.Click += new System.EventHandler(this.B_PKText_Click); + // + // B_PKLearn + // + this.B_PKLearn.Location = new System.Drawing.Point(132, 3); + this.B_PKLearn.Name = "B_PKLearn"; + this.B_PKLearn.Size = new System.Drawing.Size(123, 55); + this.B_PKLearn.TabIndex = 6; + this.B_PKLearn.Text = "Learnset Binary"; + this.B_PKLearn.UseVisualStyleBackColor = true; + this.B_PKLearn.Click += new System.EventHandler(this.B_PKLearn_Click); + // + // B_PKEvo + // + this.B_PKEvo.Location = new System.Drawing.Point(261, 3); + this.B_PKEvo.Name = "B_PKEvo"; + this.B_PKEvo.Size = new System.Drawing.Size(123, 55); + this.B_PKEvo.TabIndex = 8; + this.B_PKEvo.Text = "Evolution Binary"; + this.B_PKEvo.UseVisualStyleBackColor = true; + this.B_PKEvo.Click += new System.EventHandler(this.B_PKEvo_Click); + // + // Tab_Misc + // + this.Tab_Misc.Controls.Add(this.flowLayoutPanel3); + this.Tab_Misc.Location = new System.Drawing.Point(4, 22); + this.Tab_Misc.Name = "Tab_Misc"; + this.Tab_Misc.Padding = new System.Windows.Forms.Padding(3); + this.Tab_Misc.Size = new System.Drawing.Size(393, 253); + this.Tab_Misc.TabIndex = 2; + this.Tab_Misc.Text = "Misc"; + this.Tab_Misc.UseVisualStyleBackColor = true; + // + // flowLayoutPanel3 + // + this.flowLayoutPanel3.Controls.Add(this.B_DumpHash); + this.flowLayoutPanel3.Controls.Add(this.B_FlavorText); + this.flowLayoutPanel3.Controls.Add(this.B_EggMove); + this.flowLayoutPanel3.Controls.Add(this.B_Dex); + this.flowLayoutPanel3.Controls.Add(this.B_Outbreak); + this.flowLayoutPanel3.Controls.Add(this.B_MoveShop); + this.flowLayoutPanel3.Controls.Add(this.B_ScriptCommandNames); + this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel3.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel3.Name = "flowLayoutPanel3"; + this.flowLayoutPanel3.Size = new System.Drawing.Size(387, 247); + this.flowLayoutPanel3.TabIndex = 2; + // + // B_DumpHash + // + this.B_DumpHash.Location = new System.Drawing.Point(3, 3); + this.B_DumpHash.Name = "B_DumpHash"; + this.B_DumpHash.Size = new System.Drawing.Size(123, 55); + this.B_DumpHash.TabIndex = 14; + this.B_DumpHash.Text = "Hash Tables"; + this.B_DumpHash.UseVisualStyleBackColor = true; + this.B_DumpHash.Click += new System.EventHandler(this.B_DumpHash_Click); + // + // B_FlavorText + // + this.B_FlavorText.Location = new System.Drawing.Point(132, 3); + this.B_FlavorText.Name = "B_FlavorText"; + this.B_FlavorText.Size = new System.Drawing.Size(123, 55); + this.B_FlavorText.TabIndex = 16; + this.B_FlavorText.Text = "Flavor Text"; + this.B_FlavorText.UseVisualStyleBackColor = true; + this.B_FlavorText.Click += new System.EventHandler(this.B_FlavorText_Click); + // + // B_EggMove + // + this.B_EggMove.Location = new System.Drawing.Point(261, 3); + this.B_EggMove.Name = "B_EggMove"; + this.B_EggMove.Size = new System.Drawing.Size(123, 55); + this.B_EggMove.TabIndex = 17; + this.B_EggMove.Text = "Egg Move Entries"; + this.B_EggMove.UseVisualStyleBackColor = true; + this.B_EggMove.Click += new System.EventHandler(this.B_EggMove_Click); + // + // B_Dex + // + this.B_Dex.Location = new System.Drawing.Point(3, 64); + this.B_Dex.Name = "B_Dex"; + this.B_Dex.Size = new System.Drawing.Size(123, 55); + this.B_Dex.TabIndex = 18; + this.B_Dex.Text = "Dex Indexes"; + this.B_Dex.UseVisualStyleBackColor = true; + this.B_Dex.Click += new System.EventHandler(this.B_GetDex_Click); + // + // B_Outbreak + // + this.B_Outbreak.Location = new System.Drawing.Point(132, 64); + this.B_Outbreak.Name = "B_Outbreak"; + this.B_Outbreak.Size = new System.Drawing.Size(123, 55); + this.B_Outbreak.TabIndex = 19; + this.B_Outbreak.Text = "Outbreaks"; + this.B_Outbreak.UseVisualStyleBackColor = true; + this.B_Outbreak.Click += new System.EventHandler(this.B_GetOutbreak_Click); + // + // B_MoveShop + // + this.B_MoveShop.Location = new System.Drawing.Point(261, 64); + this.B_MoveShop.Name = "B_MoveShop"; + this.B_MoveShop.Size = new System.Drawing.Size(123, 55); + this.B_MoveShop.TabIndex = 20; + this.B_MoveShop.Text = "Move Shop"; + this.B_MoveShop.UseVisualStyleBackColor = true; + this.B_MoveShop.Click += new System.EventHandler(this.B_MoveShop_Click); + // + // B_ScriptCommandNames + // + this.B_ScriptCommandNames.Location = new System.Drawing.Point(3, 125); + this.B_ScriptCommandNames.Name = "B_ScriptCommandNames"; + this.B_ScriptCommandNames.Size = new System.Drawing.Size(123, 55); + this.B_ScriptCommandNames.TabIndex = 21; + this.B_ScriptCommandNames.Text = "Script Commands"; + this.B_ScriptCommandNames.UseVisualStyleBackColor = true; + this.B_ScriptCommandNames.Click += new System.EventHandler(this.B_DumpScriptCommands); + // + // Tab_Future + // + this.Tab_Future.Controls.Add(this.flowLayoutPanel4); + this.Tab_Future.Location = new System.Drawing.Point(4, 22); + this.Tab_Future.Name = "Tab_Future"; + this.Tab_Future.Padding = new System.Windows.Forms.Padding(3); + this.Tab_Future.Size = new System.Drawing.Size(393, 253); + this.Tab_Future.TabIndex = 3; + this.Tab_Future.Text = "Future"; + this.Tab_Future.UseVisualStyleBackColor = true; + // + // flowLayoutPanel4 + // + this.flowLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel4.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel4.Name = "flowLayoutPanel4"; + this.flowLayoutPanel4.Size = new System.Drawing.Size(387, 247); + this.flowLayoutPanel4.TabIndex = 3; + // + // B_OpenFolder + // + this.B_OpenFolder.Location = new System.Drawing.Point(322, -1); + this.B_OpenFolder.Name = "B_OpenFolder"; + this.B_OpenFolder.Size = new System.Drawing.Size(75, 23); + this.B_OpenFolder.TabIndex = 2; + this.B_OpenFolder.Text = "Open Folder"; + this.B_OpenFolder.UseVisualStyleBackColor = true; + this.B_OpenFolder.Click += new System.EventHandler(this.B_OpenFolder_Click); + // + // DumperPLA + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(401, 279); + this.Controls.Add(this.B_OpenFolder); + this.Controls.Add(this.TC_Options); + this.MaximizeBox = false; + this.Name = "DumperPLA"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "PLADump"; + this.TC_Options.ResumeLayout(false); + this.Tab_General.ResumeLayout(false); + this.flowLayoutPanel1.ResumeLayout(false); + this.Tab_PKHeX.ResumeLayout(false); + this.flowLayoutPanel2.ResumeLayout(false); + this.Tab_Misc.ResumeLayout(false); + this.flowLayoutPanel3.ResumeLayout(false); + this.Tab_Future.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl TC_Options; + private System.Windows.Forms.TabPage Tab_General; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Button B_ParsePKMDetails; + private System.Windows.Forms.Button B_DumpTrainers; + private System.Windows.Forms.Button B_ItemInfo; + private System.Windows.Forms.Button B_Moves; + private System.Windows.Forms.TabPage Tab_PKHeX; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.Button B_PKText; + private System.Windows.Forms.Button B_PKLearn; + private System.Windows.Forms.Button B_PKEvo; + private System.Windows.Forms.Button B_Gift; + private System.Windows.Forms.Button B_Static; + private System.Windows.Forms.Button B_Wild; + private System.Windows.Forms.TabPage Tab_Misc; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; + private System.Windows.Forms.Button B_DumpHash; + private System.Windows.Forms.Button B_FlavorText; + private System.Windows.Forms.Button B_EggMove; + private System.Windows.Forms.Button B_OpenFolder; + private System.Windows.Forms.TabPage Tab_Future; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; + private System.Windows.Forms.Button B_Details; + private System.Windows.Forms.Button B_Dex; + private System.Windows.Forms.Button B_Outbreak; + private System.Windows.Forms.Button B_MoveShop; + private System.Windows.Forms.Button B_ScriptCommandNames; + private System.Windows.Forms.Button B_Placement; + private System.Windows.Forms.Button B_Resident; + private System.Windows.Forms.Button B_DumpDrops; + } +} \ No newline at end of file diff --git a/pkNX.WinForms/Dumping/DumperPLA.cs b/pkNX.WinForms/Dumping/DumperPLA.cs new file mode 100644 index 00000000..3dd36df5 --- /dev/null +++ b/pkNX.WinForms/Dumping/DumperPLA.cs @@ -0,0 +1,152 @@ +using System; +using System.Diagnostics; +using System.Windows.Forms; +using pkNX.Game; + +namespace pkNX.WinForms +{ + public partial class DumperPLA : Form + { + private readonly GameDumperPLA Dumper; + + public DumperPLA(GameManagerPLA rom) + { + InitializeComponent(); + Dumper = new GameDumperPLA(rom); + } + + private void B_OpenFolder_Click(object sender, EventArgs e) => Process.Start("explorer.exe", Dumper.DumpFolder); + + #region Tab 1 + private void B_ParsePersonal_Click(object sender, EventArgs e) + { + Dumper.DumpPersonal(); + System.Media.SystemSounds.Asterisk.Play(); + } + private void B_ParsePKMDetails_Click(object sender, EventArgs e) + { + Dumper.DumpPokeInfo(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_DumpTrainers_Click(object sender, EventArgs e) + { + Dumper.DumpTrainers(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_Wild_Click(object sender, EventArgs e) + { + Dumper.DumpWilds(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_Static_Click(object sender, EventArgs e) + { + Dumper.DumpStatic(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_Gift_Click(object sender, EventArgs e) + { + Dumper.DumpGifts(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_PokeDrops_Click(object sender, EventArgs e) + { + Dumper.DumpDrops(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_ItemInfo_Click(object sender, EventArgs e) + { + Dumper.DumpItems(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_Moves_Click(object sender, EventArgs e) + { + Dumper.DumpMoves(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_Placement_Click(object sender, EventArgs e) + { + Dumper.DumpPlacement(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_Resident_Click(object sender, EventArgs e) + { + Dumper.DumpResident(); + System.Media.SystemSounds.Asterisk.Play(); + } + #endregion + + #region Tab 2 + private void B_PKText_Click(object sender, EventArgs e) + { + Dumper.DumpStrings(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_PKLearn_Click(object sender, EventArgs e) + { + Dumper.DumpLearnsetBinary(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_PKEvo_Click(object sender, EventArgs e) + { + Dumper.DumpEvolutionBinary(); + System.Media.SystemSounds.Asterisk.Play(); + } + #endregion + + #region Tab 3 + private void B_GetOutbreak_Click(object sender, EventArgs e) + { + Dumper.DumpOutbreak(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_DumpScriptCommands(object sender, EventArgs e) + { + Dumper.DumpScriptID(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_GetDex_Click(object sender, EventArgs e) + { + Dumper.DumpDex(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_MoveShop_Click(object sender, EventArgs e) + { + Dumper.DumpMoveShop(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_DumpHash_Click(object sender, EventArgs e) + { + Dumper.DumpAHTB(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_FlavorText_Click(object sender, EventArgs e) + { + Dumper.DumpFlavorText(); + System.Media.SystemSounds.Asterisk.Play(); + } + + private void B_EggMove_Click(object sender, EventArgs e) + { + Dumper.DumpEggEntries(); + System.Media.SystemSounds.Asterisk.Play(); + } + #endregion + + } +} diff --git a/pkNX.WinForms/Dumping/GameDumperPLA.cs b/pkNX.WinForms/Dumping/GameDumperPLA.cs new file mode 100644 index 00000000..0834a508 --- /dev/null +++ b/pkNX.WinForms/Dumping/GameDumperPLA.cs @@ -0,0 +1,1048 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using pkNX.Containers; +using pkNX.Game; +using pkNX.Structures; +using pkNX.Structures.FlatBuffers; + +namespace pkNX.WinForms +{ + public class GameDumperPLA + { + private readonly GameManagerPLA ROM; + public GameDumperPLA(GameManagerPLA rom) => ROM = rom; + public string DumpFolder => Path.Combine(Directory.GetParent(ROM.PathRomFS).FullName, "Dump"); + + private string GetPath(string path) + { + Directory.CreateDirectory(DumpFolder); + var result = Path.Combine(DumpFolder, path); + Directory.CreateDirectory(Directory.GetParent(result).FullName); // double check :( + return result; + } + + private string GetPath(string parent, string path) + { + Directory.CreateDirectory(DumpFolder); + var result = Path.Combine(DumpFolder, parent, path); + Directory.CreateDirectory(Directory.GetParent(result).FullName); // double check :( + return result; + } + + public void DumpPersonal() + { + var data = ROM.GetFile(GameFile.PersonalStats)[0]; + var obj = FlatBufferConverter.DeserializeFrom(data); + var test = PersonalConverter.GetBin(obj); + var path = GetPath("personal_la"); + File.WriteAllBytes(path, test); + + var csv = GetPath("personal.csv"); + File.WriteAllText(csv, FlatDumper.GetTable(data)); + } + + public void DumpPokeInfo() + { + var s = ROM.GetStrings(TextName.SpeciesNames); + + var lrd = ROM.GetFile(GameFile.Learnsets)[0]; + var lr = FlatBufferConverter.DeserializeFrom(lrd); + var evd = ROM.GetFilteredFolder(GameFile.Evolutions)[0]; + var ev = FlatBufferConverter.DeserializeFrom(evd); + var pt = GetPersonal(); + var altForms = pt.GetFormList(s, pt.MaxSpeciesID); + var entryNames = pt.GetPersonalEntryList(altForms, s, pt.MaxSpeciesID, out _, out _); + var moveNames = ROM.GetStrings(TextName.MoveNames); + + var pd = new PersonalDumperPLA + { + Colors = Enum.GetNames(typeof(PokeColor)), + EggGroups = Enum.GetNames(typeof(EggGroup)), + EntryEggMoves = Array.Empty(), + EntryLearnsets = lr.Table, + EntryNames = entryNames, + ExpGroups = Enum.GetNames(typeof(EXPGroup)), + Evos = ev.Table, + + Abilities = ROM.GetStrings(TextName.AbilityNames), + Items = ROM.GetStrings(TextName.ItemNames), + Moves = moveNames, + Types = ROM.GetStrings(TextName.Types), + Species = ROM.GetStrings(TextName.SpeciesNames), + ZukanA = ROM.GetStrings(TextName.PokedexEntry1), + ZukanB = ROM.GetStrings(TextName.PokedexEntry2), + TMIndexes = Legal.TMHM_SWSH, + }; + + var result = pd.Dump(pt); + + var outname = GetPath("Pokemon.txt"); + File.WriteAllLines(outname, result); + + var learnTable = pd.MoveSpeciesLearn; + var outLearn = GetPath("MovePerPokemon.txt"); + var moveLines = learnTable.Select((z, i) => $"{i:000}\t{moveNames[i]}\t{string.Join(", ", z.Distinct())}"); + File.WriteAllLines(outLearn, moveLines); + + DumpMoveUsers(pt, lr); + } + + private void DumpMoveUsers(PersonalTable pt, Learnset8a lr) + { + List Users = new(); + var moves = ROM.GetStrings(TextName.MoveNames); + var spec = ROM.GetStrings(TextName.SpeciesNames); + var shop = Legal.MoveShop8a; + for (int i = 0; i < moves.Length; i++) + { + var move = i; + var shopIndex = Array.IndexOf(shop, move); + bool isShop = shopIndex != -1; + var learn = lr.Table.Where(z => z.Arceus.Any(x => x.Move == move)); + var filtered = learn.Where(z => ((PersonalInfoLA)pt.GetFormeEntry(z.Species, z.Form)).IsPresentInGame); + var result = filtered.Select(x => $"{spec[x.Species]}{(x.Form == 0 ? "" : $"-{x.Form}")} @ {Array.Find(x.Arceus, w => w.Move == move).Level}").ToArray(); + + List r = new() { $"{moves[move]}:" }; + if (isShop) + { + var species = pt.Table.OfType ().Where(z => z.SpecialTutors[0][shopIndex] && z.IsPresentInGame); + var names = species.Select(z => $"{spec[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")}"); + r.Add($"\tTutors: {string.Join(", ", names)}"); + } + + var lv = result.Length == 0 ? "None." : string.Join(", ", result); + r.Add($"\tLevel Up: {lv}"); + Users.AddRange(r); + } + + var outname = GetPath("MoveUsers.txt"); + File.WriteAllLines(outname, Users); + } + + private static readonly string[] ahtbext = { ".tbl", ".hsh" }; + + public void DumpAHTB() + { + var files = Directory.EnumerateFiles(ROM.PathRomFS, "*", SearchOption.AllDirectories) + .Where(z => ahtbext.Contains(Path.GetExtension(z))); + + var result = new HashSet(); + var list = new List(); + var gf = new List(); + foreach (var f in files) + { + var bytes = File.ReadAllBytes(f); + if (AHTB.IsAHTB(bytes)) + { + var tbl = new AHTB(bytes); + var summaries = tbl.Summary; + foreach (var t in tbl.ShortSummary) + result.Add(t); + list.Add(Path.GetFileName(f)); + list.AddRange(summaries); + } + else if (DatTable.IsDatTable(bytes)) + { + var tbl = new DatTable(bytes); + var summaries = tbl.Summary; + foreach (var t in tbl.ShortSummary) + result.Add(t); + list.Add(Path.GetFileName(f)); + list.AddRange(summaries); + } + } + + var paks = Directory.EnumerateFiles(ROM.PathRomFS, "*", SearchOption.AllDirectories) + .Where(z => Path.GetExtension(z) == ".gfpak"); + foreach (var f in paks) + { + var pak = new GFPack(f); + foreach (var bytes in pak.DecompressedFiles) + { + if (AHTB.IsAHTB(bytes)) + { + var tbl = new AHTB(bytes); + var summaries = tbl.Summary; + foreach (var t in tbl.ShortSummary) + result.Add(t); + list.Add(Path.GetFileName(f)); + list.AddRange(summaries); + } + } + + for (var i = 0; i < pak.HashAbsolute.Length; i++) + { + var x = pak.HashAbsolute[i]; + gf.Add($"{x.HashFnv1aPathFull:X16}\t{f}.Absolute[{i}]"); + } + + for (var i = 0; i < pak.HashInFolder.Length; i++) + { + var x = pak.HashInFolder[i]; + var folder = x.Folder; + gf.Add($"{folder.HashFnv1aPathFolderName:X16}\t{f}.Folder[{i}] ({folder.FileCount})"); + for (int j = 0; j < x.Files.Length; j++) + { + var y = x.Files[j]; + gf.Add($"{y.HashFnv1aPathFileName:X16}\t{f}.Folder[{i}][{j}] ({y.Index})"); + } + } + } + + var outname = GetPath("ahtb.txt"); + var outname2 = GetPath("ahtblist.txt"); + var outname3 = GetPath("gfpakhash.txt"); + File.WriteAllLines(outname, result); + File.WriteAllLines(outname2, list); + File.WriteAllLines(outname3, gf); + } + + public static Dictionary ReadAHTB(byte[] bytes) + { + if (!AHTB.IsAHTB(bytes)) + throw new ArgumentException(); + + var tbl = new AHTB(bytes); + return tbl.ToDictionary(); + } + + private TrainerEditor GetTrainerEditor() + { + var editor = new TrainerEditor + { + ReadClass = data => new TrainerClass8(data), + ReadPoke = data => new TrainerPoke8(data), + ReadTrainer = data => new TrainerData8(data), + ReadTeam = TrainerPoke8.ReadTeam, + WriteTeam = TrainerPoke8.WriteTeam, + TrainerData = ROM.GetFilteredFolder(GameFile.TrainerData), + TrainerPoke = ROM.GetFilteredFolder(GameFile.TrainerPoke), + TrainerClass = ROM.GetFilteredFolder(GameFile.TrainerClass), + }; + editor.Initialize(); + return editor; + } + + public void DumpTrainers() + { + var trc = ROM.GetStrings(TextName.TrainerClasses); + var trn = ROM.GetStrings(TextName.TrainerNames); + var m = ROM.GetStrings(TextName.MoveNames); + var s = ROM.GetStrings(TextName.SpeciesNames); + + var tr = GetTrainerEditor(); + var result = new List(); + for (int i = 0; i < tr.Length; i++) + { + var t = tr[i]; + + // some battles with Avery and Klara have out of bounds trclasses -- set back to "Pokémon Trainer" + if (t.Self.Class > trc.Length) + t.Self.Class = 1; + + result.Add($"{i:000} - {trc[t.Self.Class]}: {trn[i]}"); + const int MoneyScalar = 80; + result.Add($"AI: {t.Self.AI} | Mode: {t.Self.Mode} | Money: {t.Self.Money * MoneyScalar}"); + result.Add($"Pokémon Count: {t.Self.NumPokemon}"); + + result.Add("---"); + for (int j = 0; j < t.Team.Count; j++) + { + IEnumerable moves = t.Team[j].Moves.Where(z => z != 0).Select(z => m[z]).ToArray(); + if (!moves.Any()) moves = new[] { "Default Level Up" }; + int form = t.Team[j].Form; + var formstr = form != 0 ? $"-{form}" : ""; + var str = $"{j + 1}: Lv{t.Team[j].Level:00} {s[t.Team[j].Species]}{formstr} : {string.Join(" / ", moves)}"; + result.Add(str); + } + result.Add("---"); + + result.Add("========="); + result.Add(""); + } + + var outname = GetPath("trparse.txt"); + File.WriteAllLines(outname, result); + } + + public void DumpDrops() + { + var names = ROM.GetStrings(TextName.ItemNames); + var field = Path.Combine(ROM.PathRomFS, "bin", "pokemon", "data", "poke_drop_item.bin"); + var fieldItems = FlatBufferConverter.DeserializeFrom(field).Table.Select(z => z.Dump(names)); + File.WriteAllLines(GetPath("DropItems.txt"), fieldItems); + + var battle = Path.Combine(ROM.PathRomFS, "bin", "pokemon", "data", "poke_drop_item_battle.bin"); + var battleItems = FlatBufferConverter.DeserializeFrom(battle).Table.Select(z => z.Dump(names)); + File.WriteAllLines(GetPath("BattleDropItems.txt"), battleItems); + } + + public void DumpItems() + { + var items = ROM.GetFile(GameFile.ItemStats); + var file = items[0]; + var array = Item8a.GetArray(file); + var groups = array.GroupBy(z => z.Pouch); + foreach (var g in groups) + { + var key = g.Key; + var IDs = g.Where(z => z.ItemSprite >= 0).Select(z => z.ItemID); + var path = GetPath($"{key}.txt"); + File.WriteAllText(path, string.Join(", ", IDs.Select(z => $"{z:000}"))); + } + + var names = ROM.GetStrings(TextName.ItemNames); + var lines = TableUtil.GetNamedTypeTable(array, names, "Items"); + var table = GetPath("ItemData.txt"); + var bin = GetPath("ItemData.bin"); + File.WriteAllText(table, lines); + File.WriteAllBytes(bin, array.SelectMany(z => z.Data).ToArray()); + } + + public void DumpMoves() + { + var dir = Path.Combine(ROM.PathRomFS, "bin", "pml", "waza"); + var files = Directory.GetFiles(dir); + var moves = FlatBufferConverter.DeserializeFrom(files); + var names = ROM.GetStrings(TextName.MoveNames); + var lines = TableUtil.GetNamedTypeTable(moves, names, "Moves"); + var table = GetPath("MoveData.txt"); + File.WriteAllText(table, lines); + + var pp = moves.Select(z => z.FPP); + var str = string.Join(", ", pp.Select(z => $"{z:00}")); + var pppath = GetPath("MovePP.txt"); + File.WriteAllText(pppath, str); + + // get dummied moves + var moveNames = ROM.GetStrings(TextName.MoveNames); + var moveDesc = ROM.GetStrings(TextName.MoveFlavor); + + var tuple = moveNames + .Select((z, i) => new { Name = z, Desc = moveDesc[i], Index = i }) + .Where(z => !moves[z.Index].CanUseMove) + .Select(z => $"{z.Index:000}\t{z.Name}"); + + var path = GetPath("snappedMoves.txt"); + File.WriteAllLines(path, tuple); // rest in peace + + var snap2 = GetPath("snappedID.txt"); + var snapMove = string.Join(", ", moves.Where(z => !z.CanUseMove).Select(z => $"{z.MoveID:000}")); + File.WriteAllText(snap2, snapMove); + } + + public void DumpLearnsetBinary() + { + var data = ROM.GetFile(GameFile.Learnsets)[0]; + var obj = FlatBufferConverter.DeserializeFrom(data); + var pt = GetPersonal(); + var result = new byte[pt.TableLength][]; + for (int i = 0; i < result.Length; i++) + result[i] = Array.Empty(); + + foreach (var e in obj.Table) + { + if (e.Arceus.Length == 0) + continue; + var index = pt.GetFormeIndex(e.Species, e.Form); + var entry = (PersonalInfoLA)pt[index]; + if (!entry.IsPresentInGame) + continue; + result[index] = e.WriteAsLearn6(); + } + + var mini = MiniUtil.PackMini(result, "la"); + var bin = GetPath(Path.Combine("bin", "lvlmove_la.pkl")); + File.WriteAllBytes(bin, mini); + } + + private PersonalTable GetPersonal() + { + var pd = ROM.GetFile(GameFile.PersonalStats)[0]; + var po = FlatBufferConverter.DeserializeFrom(pd); + var test = PersonalConverter.FromArceus(po); + return new PersonalTable(test, 905); + } + + public void DumpEvolutionBinary() + { + // format matches past gen and PKHeX's expected format + var data = ROM.GetFilteredFolder(GameFile.Evolutions)[0]; + var obj = FlatBufferConverter.DeserializeFrom(data); + var pt = GetPersonal(); + var result = new byte[pt.TableLength][]; + for (int i = 0; i < result.Length; i++) + result[i] = Array.Empty(); + + for (var i = 0; i < obj.Table.Length; i++) + { + var e = obj.Table[i]; + if (e.Table?.Length is not >0) + continue; + var index = pt.GetFormeIndex(e.Index, e.Form); + var entry = (PersonalInfoLA)pt[index]; + if (!entry.IsPresentInGame) + continue; + result[index] = e.Write(); + } + + var mini = MiniUtil.PackMini(result, "la"); + var bin = GetPath(Path.Combine("bin", "evos_la.pkl")); + File.WriteAllBytes(bin, mini); + } + + public void DumpGifts() + { + var speciesNames = ROM.GetStrings(TextName.SpeciesNames); + var data = ROM.GetFile(GameFile.EncounterGift)[0]; + var gifts = FlatBufferConverter.DeserializeFrom(data); + var table = TableUtil.GetTable(gifts.Table); + var fn = GetPath("Gifts.txt"); + File.WriteAllText(fn, table); + + var f2 = GetPath("GiftsPKHeX.txt"); + File.WriteAllLines(f2, gifts.Table.Select(z => z.Dump(speciesNames))); + } + + public void DumpStatic() + { + var speciesNames = ROM.GetStrings(TextName.SpeciesNames); + var data = ROM.GetFile(GameFile.EncounterStatic)[0]; + var statics = FlatBufferConverter.DeserializeFrom(data); + + var lines = new List(); + + foreach (var set in statics.Table) + { + lines.Add($"{set.EncounterName}:"); + + var table = TableUtil.GetTable(set.Table); + + foreach (var line in table.Split(new [] { Environment.NewLine }, StringSplitOptions.None)) + lines.Add($"\t{line}"); + } + + var fn = GetPath("StaticEncounters.txt"); + File.WriteAllLines(fn, lines); + + var f2 = GetPath("StaticEncountersPKHeX.txt"); + File.WriteAllLines(f2, statics.Table.SelectMany(z => z.Table?.Select(x => x.Dump(speciesNames, z.EncounterName))).OrderBy(z => z)); + } + + public void DumpWilds() + { + var speciesName = ROM.GetStrings(TextName.SpeciesNames); + + Dictionary map = GetPlaceNameMap(); + var multdata = ROM.GetFile(GameFile.EncounterRateTable)[0]; + var multipliers = FlatBufferConverter.DeserializeFrom(multdata); + var miscdata = ROM.GetFile(GameFile.PokeMisc)[0]; + var misc = FlatBufferConverter.DeserializeFrom(miscdata); + + var residentpak = ROM.GetFile(GameFile.Resident)[0]; + var resident = new GFPack(residentpak); + + const string wild = "wild"; + Directory.CreateDirectory(GetPath(wild)); + + var all = new List(); + var allUnownLines = new List(); + var allUnownLinesBias = new List(); + const float bias = 20; + + var hexBin = new List(); + var allSlots = new List(); + var allSpawners = new List(); + var allWormholes = new List(); + var allLocations = new List(); + var allLandItems = new List(); + var allLandMarks = new List(); + var allUnown = new List(); + foreach (var areaNameList in PLAInfo.AreaNames) + { + var instance = AreaInstance8a.Create(resident, areaNameList); + var lines = EncounterTable8aUtil.GetLines(multipliers, misc, speciesName, instance, map).ToList(); + File.WriteAllLines(GetPath(wild, $"Encounters_{instance.AreaName}.txt"), lines); + + var unown = EncounterTable8aUtil.GetUnownLines(instance, map).Distinct().ToList(); + File.WriteAllLines(GetPath(wild, $"unown_0_{instance.AreaName}.txt"), unown); + var unownBias = EncounterTable8aUtil.GetUnownLinesBias(instance, map, bias).Distinct().ToList(); + File.WriteAllLines(GetPath(wild, $"unown_{bias:0}_{instance.AreaName}.txt"), unownBias); + allUnownLines.AddRange(unown); + allUnownLinesBias.AddRange(unownBias); + + var slices = EncounterTable8aUtil.GetEncounterDump(instance, map, misc); + foreach (var s in slices) + { + if (!hexBin.Any(z => z.SequenceEqual(s))) + hexBin.Add(s); + } + + all.AddRange(lines); + all.Add(string.Empty); + + allSlots.AddRange(instance.Encounters.Table.SelectMany(z => z.Table)); + allSpawners.AddRange(instance.Spawners); + allWormholes.AddRange(instance.Wormholes); + allLocations.AddRange(instance.Locations); + allLandItems.AddRange(instance.LandItems); + allLandMarks.AddRange(instance.LandMarks); + allUnown.AddRange(instance.Unown); + + foreach (var subArea in instance.SubAreas) + { + allSlots.AddRange(subArea.Encounters.Table.SelectMany(z => z.Table)); + allSpawners.AddRange(subArea.Spawners); + allWormholes.AddRange(subArea.Wormholes); + allLocations.AddRange(subArea.Locations); + allLandItems.AddRange(subArea.LandItems); + allLandMarks.AddRange(subArea.LandMarks); + } + } + + var mini = MiniUtil.PackMini(hexBin.ToArray(), "la"); + File.WriteAllBytes(GetPath(wild, "encounter_la.pkl"), mini); + File.WriteAllLines(GetPath(wild, "Encounters_All.txt"), all); + File.WriteAllLines(GetPath(wild, "Unown_All.txt"), allUnownLines); + File.WriteAllLines(GetPath(wild, $"Unown_All_Bias_{bias}.txt"), allUnownLinesBias); + + File.WriteAllText(GetPath(wild, "allMultipliers.csv"), TableUtil.GetTable(multipliers.Table)); + File.WriteAllText(GetPath(wild, "PokeMisc.csv"), TableUtil.GetTable(misc.Table)); + File.WriteAllText(GetPath(wild, "allSlotTable.csv"), TableUtil.GetTable(allSlots)); + File.WriteAllText(GetPath(wild, "allSpawnerTable.csv"), TableUtil.GetTable(allSpawners)); + File.WriteAllText(GetPath(wild, "allWormholeTable.csv"), TableUtil.GetTable(allWormholes)); + File.WriteAllText(GetPath(wild, "allLocationTable.csv"), TableUtil.GetTable(allLocations)); + File.WriteAllText(GetPath(wild, "allLandItems.csv"), TableUtil.GetTable(allLandItems)); + File.WriteAllText(GetPath(wild, "allLandMarks.csv"), TableUtil.GetTable(allLandMarks)); + File.WriteAllText(GetPath(wild, "allLandMarkSpawns.csv"), TableUtil.GetTable(allLandMarks)); + File.WriteAllText(GetPath(wild, "allUnown.csv"), TableUtil.GetTable(allUnown)); + } + + public void DumpResident() + { + var residentpak = ROM.GetFile(GameFile.Resident)[0]; + var resident = new GFPack(residentpak); + var settings = FlatBufferConverter.DeserializeFrom(resident[2042]); + var dir = GetPath("Resident"); + var props = typeof(AreaSettings8a).GetProperties(); + foreach (var x in settings.Table) + { + foreach (var p in props) + { + var value = p.GetValue(x); + if (value is not string {Length: not 0} s) + continue; + if (!s.Contains('/')) + continue; + if (File.Exists(Path.Combine(ROM.PathRomFS, s))) + continue; + + try + { + int index = resident.GetIndexFull(FnvHash.HashFnv1a_64(s)); + if (index == -1) + continue; + var data = resident.GetDataFullPath(s); + var file = s.Replace('/', '\\'); + var dest = Path.Combine(dir, file); + var folder = Path.GetDirectoryName(dest); + Directory.CreateDirectory(folder); + File.WriteAllBytes(dest, data); + } + catch + { + } + } + } + } + + public void DumpPlacement() + { + var residentpak = ROM.GetFile(GameFile.Resident)[0]; + var resident = new GFPack(residentpak); + + Dictionary map = GetPlaceNameMap(); + + var location_all = new List(); + var spawner_all = new List(); + var wh_spawner_all = new List(); + var mkrg_all = new List(); + const string placement = "placement"; + Directory.CreateDirectory(GetPath(placement)); + + foreach (var areaNameList in PLAInfo.AreaNames) + { + var area = AreaInstance8a.Create(resident, areaNameList); + foreach (var subArea in new[] { area }.Concat(area.SubAreas)) + { + var areaName = subArea.AreaName; + if (subArea.Locations.Length != 0) + { + var loc_lines = GetLocationBoundLines(areaName, map, subArea.Locations); + + location_all.AddRange(loc_lines); + location_all.Add(string.Empty); + File.WriteAllLines(GetPath(placement, $"Location_{areaName}.txt"), loc_lines); + } + if (subArea.Spawners.Length != 0) + { + var spwn_lines = GetSpawnLines(areaName, map, subArea.Spawners, subArea.Locations); + + spawner_all.AddRange(spwn_lines); + spawner_all.Add(string.Empty); + File.WriteAllLines(GetPath(placement, $"Spawner_{areaName}.txt"), spwn_lines); + } + if (subArea.Wormholes.Length != 0) + { + var spwn_lines = GetSpawnLines(areaName, map, subArea.Wormholes, subArea.Locations); + + wh_spawner_all.AddRange(spwn_lines); + wh_spawner_all.Add(string.Empty); + File.WriteAllLines(GetPath(placement, $"WhSpawner_{areaName}.txt"), spwn_lines); + } + if (subArea.Mikaruge.Length != 0) + { + var mkrg_lines = GetMikarugeLines(areaName, map, subArea.Mikaruge, subArea.Locations); + + mkrg_all.AddRange(mkrg_lines); + mkrg_all.Add(string.Empty); + File.WriteAllLines(GetPath(placement, $"Mikaruge_{areaName}.txt"), mkrg_lines); + } + + // Debug for Visualization + if (new[] { "ha_area01", "ha_area02", "ha_area03", "ha_area04", "ha_area05" }.Contains(areaName)) + DumpVisualizationData(area); + } + } + + File.WriteAllLines(GetPath(placement, "Location_all.txt"), location_all); + File.WriteAllLines(GetPath(placement, "Spawner_all.txt"), spawner_all); + File.WriteAllLines(GetPath(placement, "WhSpawner_all.txt"), wh_spawner_all); + File.WriteAllLines(GetPath(placement, "Mikaruge_all.txt"), mkrg_all); + } + + private Dictionary GetPlaceNameMap() + { + var map = new Dictionary(); + var place_names = ROM.GetStrings(TextName.metlist_00000); + + var scriptFolder = new FolderContainer(((FolderContainer)ROM.GetFile(GameFile.StoryText)).FilePath!); + scriptFolder.Initialize(); + + var locationNames = scriptFolder.GetFileData("place_name.tbl")!; + var ahtb = new AHTB(locationNames); + for (var i = 0; i < place_names.Length; i++) + map[ahtb.Entries[i].Name] = (place_names[i], i); + return map; + } + + private static IReadOnlyList GetLocationBoundLines(string areaName, + IReadOnlyDictionary map, + IEnumerable locations) + { + var result = new List { $"Area: {areaName}" }; + foreach (var location in locations) + { + string line = $"\t{location}"; + if (location.IsNamedPlace) + { + var (name, index) = map[location.PlaceName]; + line += $" // {index}, \"{name}\""; + } + result.Add(line); + } + return result; + } + + private static IReadOnlyList GetSpawnLines(string areaName, + IReadOnlyDictionary map, + IEnumerable spawners, + IReadOnlyList locations) + { + var result = new List { $"Area: {areaName}" }; + foreach (var spawner in spawners) + { + var contained = GetNearbyLocationNames(spawner, locations, map); + result.Add($"\t{spawner} // Containing Locations: {contained}"); + } + return result; + } + + private static IReadOnlyList GetMikarugeLines(string areaName, + IReadOnlyDictionary map, + IEnumerable mkrgs, + IReadOnlyList locations) + { + var result = new List { $"Area: {areaName}" }; + foreach (var mkrg in mkrgs) + { + var contained = GetNearbyLocationNames(mkrg, locations, map); + result.Add($"\t{mkrg} // Containing Locations: {contained}"); + } + return result; + } + + private static string GetNearbyLocationNames(PlacementSpawner8a spawner, + IReadOnlyList locations, + IReadOnlyDictionary map) + { + var containedBy = spawner.GetContainingLocations(locations); + var placeNames = containedBy.Select(z => z.PlaceName).Distinct(); + var localized = placeNames.Select(pn => map[pn].Name); + return string.Join(", ", localized); + } + + private static string GetNearbyLocationNames(PlacementMkrgEntry mkrg, + IReadOnlyList locations, + IReadOnlyDictionary map) + { + var containedBy = mkrg.GetContainingLocations(locations); + var placeNames = containedBy.Select(z => z.PlaceName).Distinct(); + var localized = placeNames.Select(pn => map[pn].Name); + return string.Join(", ", localized); + } + + private void DumpVisualizationData(AreaInstance8a area) + { + var vis_lines = new List { "LOCS = [" }; + + const string folder = "placementVis"; + Directory.CreateDirectory(GetPath(folder)); + + foreach (var loc in area.Locations) + { + if (!loc.IsNamedPlace) + continue; + vis_lines.Add($"({loc.Parameters.Coordinates.ToTriple()}, {loc.Parameters.Rotation.ToTriple()}, {loc.ShapeSummary.Replace("/* Shape = */ ", "")}, {loc.SizeX}, {loc.SizeY}, {loc.SizeZ}, \"{loc.PlaceName}\"), "); + } + + vis_lines.Add("]"); + vis_lines.Add(string.Empty); + + vis_lines.Add("SPAWNERS = ["); + foreach (var spwn in area.Spawners) + vis_lines.Add($"({spwn.Parameters.Coordinates.ToTriple()}, {spwn.Scalar}),"); + vis_lines.Add("]"); + vis_lines.Add(string.Empty); + + vis_lines.Add("WH_SPAWNERS = ["); + foreach (var spwn in area.Wormholes) + vis_lines.Add($"({spwn.Parameters.Coordinates.ToTriple()}, {spwn.Scalar}),"); + + vis_lines.Add("LANDMARKS = ["); + foreach (var spwn in area.LandMarks) + vis_lines.Add($"({spwn.Parameters.Coordinates.ToTriple()}, {spwn.Scalar}),"); + + vis_lines.Add("]"); + vis_lines.Add(string.Empty); + + File.WriteAllLines(GetPath(folder, $"Vis_{area.AreaName}.txt"), vis_lines); + } + + public void DumpOutbreak() + { + var file = ROM.GetFile(GameFile.Outbreak).FilePath; + var result = FlatDumper.GetTable(file!); + File.WriteAllText(GetPath("massOutbreak.txt"), result); + + var arr = FlatBufferConverter.DeserializeFrom(file!).Table; + var cache = new DataCache(arr); + var names = Enumerable.Range(0, cache.Length).Select(z => $"{z}").ToArray(); + var form = new GenericEditor(cache, names, "Outbreak"); + form.ShowDialog(); + } + + public void DumpDex() + { + DumpDexSummarySpecies(); + + var dexResearchPath = Path.Combine(ROM.PathRomFS, "bin", "appli", "pokedex", "res_table", "pokedex_research_task_table.bin"); + var dexResearchBin = File.ReadAllBytes(dexResearchPath); + var dexResearch = FlatBufferConverter.DeserializeFrom(dexResearchBin); + + var csv = GetPath("pokedex_research.csv"); + File.WriteAllText(csv, TableUtil.GetTable(dexResearch.Table)); + + // Pokedex Research for PKHeX + DumpResearchTasks(dexResearch); + } + + private void DumpResearchTasks(PokedexResearchTable dexResearch) + { + var pt = GetPersonal(); + var result = new byte[pt.Table.Max(p => ((PersonalInfoLA)p).DexIndexHisui)][]; + for (int i = 0; i < result.Length; i++) + result[i] = Array.Empty(); + + int GetDexIndex(int species) + { + var formCount = pt.GetFormeEntry(species, 0).FormeCount; + for (var form = 0; form < formCount; form++) + { + var p = (PersonalInfoLA)pt.GetFormeEntry(species, form); + + if (p.DexIndexHisui != 0) + return p.DexIndexHisui; + } + + return 0; + } + + for (var species = 0; species <= 980; species++) + { + var entries = dexResearch.Table.Where(e => e.Species == species); + if (!entries.Any()) + continue; + + var dexInd = GetDexIndex(species); + if (dexInd == 0) + throw new ArgumentException($"Research tasks exist for species {species} not in dex?"); + + if (result[dexInd - 1].Length != 0) + throw new ArgumentException($"Two species share dex index {dexInd}?"); + + using var ms = new MemoryStream(); + using var br = new BinaryWriter(ms); + + var moveTaskIndex = 0; + var defeatTypeTaskIndex = 0; + + foreach (var task in entries) + { + var type = task.Type; + var timeOfDay = task.TimeOfDay; + + var curMultiIndex = 0xFF; + + if (task.Task == 1) + { + curMultiIndex = moveTaskIndex; + moveTaskIndex++; + } + + if (task.Task == 2) + { + curMultiIndex = defeatTypeTaskIndex; + defeatTypeTaskIndex++; + } + else + { + type = 18; + } + + if (task.Task != 10) + { + timeOfDay = 5; + } + + var thresholds = + new[] { task.Threshold1, task.Threshold2, task.Threshold3, task.Threshold4, task.Threshold5 } + .Where(e => e != 0).ToArray(); + + // 00: u8 task + // 01: u8 points_single + // 02: u8 points_bonus + // 03: u8 threshold + // 04: u16 move + // 06: u8 type + // 07: u8 time of day + // 08: u64 hash_06 + // 10: u64 hash_07 + // 18: u64 hash_08 + // 20: u8 num_thresholds + // 21: u8 thresholds[5] + // 26: u8 required + // 27: u8 multi_index + + br.Write((byte)task.Task); + br.Write((byte)task.PointsSingle); + br.Write((byte)task.PointsBonus); + br.Write((byte)task.Threshold); + br.Write((ushort)task.Move); + br.Write((byte)type); + br.Write((byte)timeOfDay); + br.Write(task.Hash_06); + br.Write(task.Hash_07); + br.Write(task.Hash_08); + + br.Write((byte)thresholds.Length); + for (var i = 0; i < 5; i++) + { + if (i < thresholds.Length) + br.Write((byte)thresholds[i]); + else + br.Write((byte)0); + } + + br.Write((byte)(task.RequiredForCompletion ? 1 : 0)); + br.Write((byte)curMultiIndex); + } + + result[dexInd - 1] = ms.ToArray(); + } + + var mini = MiniUtil.PackMini(result, "la"); + var bin = GetPath(Path.Combine("bin", "researchtask_la.pkl")); + File.WriteAllBytes(bin, mini); + } + + private void DumpDexSummarySpecies() + { + var pt = GetPersonal(); + var s = ROM.GetStrings(TextName.SpeciesNames); + + var dex = new List(); + var dexit = new List(); + var foreign = new List(); + for (int i = 1; i < pt.TableLength; i++) + { + var p = (PersonalInfoLA)pt[i]; + bool any = false; + var specForm = $"{p.Species:000}\t{p.Form}\t{s[p.Species]}{(p.Form == 0 ? "" : $"-{p.Form:00}")}"; + if (p.DexIndexHisui != 0) + { + dex.Add($"{p.DexIndexHisui:000}\t{specForm}"); + any = true; + } + + if (!p.IsPresentInGame) + dexit.Add(specForm); + else if (!any) + foreign.Add(specForm); + } + + var path = GetPath("Dex.txt"); + File.WriteAllLines(path, dex.OrderBy(z => z)); + + var path4 = GetPath("Dexit.txt"); + File.WriteAllLines(path4, dexit); + + var path5 = GetPath("Foreign.txt"); + File.WriteAllLines(path5, foreign); + } + + public void DumpFlavorText() + { + IEnumerable Zip(TextName name, TextName flavor) + { + var n = ROM.GetStrings(name); + var f = ROM.GetStrings(flavor); + return n.Select((z, i) => $"{z}\t{f[i].Replace("\\n", " ")}"); + } + + var p1 = GetPath("ItemFlavor.txt"); + var p2 = GetPath("MoveFlavor.txt"); + var p3 = GetPath("AbilityFlavor.txt"); + + var l1 = Zip(TextName.ItemNames, TextName.ItemFlavor); + var l2 = Zip(TextName.MoveNames, TextName.MoveFlavor); + var l3 = Zip(TextName.AbilityNames, TextName.AbilityFlavor); + + File.WriteAllLines(p1, l1); + File.WriteAllLines(p2, l2); + File.WriteAllLines(p3, l3); + } + + public void DumpEggEntries() + { + } + + public static byte[] GetDistributionContents(string path, out int index) + { + var archive = File.ReadAllBytes(path); + + // Validate Header + if (archive.Length < 0x20 || archive.Length != 4 + BitConverter.ToInt32(archive, 0x10) || BitConverter.ToInt32(archive, 0x8) != 0x20) + throw new ArgumentException(); + + index = archive[0]; + + // TODO: Eventually validate CRC16 over Data[:-4], CRC stored at Data[-4:] + + var data = new byte[archive.Length - 0x24]; + Array.Copy(archive, 0x20, data, 0, data.Length); + return data; + } + + private static readonly string[] LanguageCodes = { "ja", "en", "fr", "it", "de", "es", "ko", "zh", "zh2" }; + + private static readonly string[] LanguageNames = + { + "カタカナ", + "漢字", + "English", + "Français", + "Italiano", + "Deutsch", + "Español", + "한국", + "汉字简化方案", + "漢字簡化方案", + }; + + private void ChangeLanguage(int index) + { + ROM.Language = index; + ROM.ResetText(); + } + + public void DumpStrings() + { + int lang = ROM.Language; + var indexes = new[] { 0, 2, 3, 4, 5, 6, 7, 8, 9 }; + for (int i = 0; i < indexes.Length; i++) + { + var code = LanguageCodes[i]; + var name = LanguageNames[i]; + var index = indexes[i]; + + DumpStrings(code, name, index); + } + ChangeLanguage(lang); + } + + private void DumpStrings(string code, string name, int index) + { + Console.WriteLine($"Dumping strings for {name}."); + ChangeLanguage(index); + + DumpStringSet(TextName.MoveNames, "Moves"); + DumpStringSet(TextName.ItemNames, "Items"); + DumpStringSet(TextName.SpeciesNames, "Species"); + DumpStringSet(TextName.AbilityNames, "Abilities"); + DumpStringSet(TextName.metlist_00000, "la_00000"); + DumpStringSet(TextName.metlist_30000, "la_30000"); + DumpStringSet(TextName.metlist_40000, "la_40000"); + DumpStringSet(TextName.metlist_60000, "la_60000"); + DumpStringSet(TextName.Forms, "forms"); + + void DumpStringSet(TextName t, string file) + { + var strings = ROM.GetStrings(t); + var fn = $"text_{file}_{code}.txt"; + + var folder = Path.Combine(code, fn); + + var path = GetPath(folder); + File.WriteAllLines(path, strings); + } + } + + public void DumpScriptID() + { + var file = Path.Combine(ROM.PathRomFS, "bin", "event", "script_id_record_release.bin"); + var text = FlatDumper.GetTable(file); + var path = GetPath("scriptCommands.txt"); + File.WriteAllText(path, text); + } + + public void DumpMoveShop() + { + var file = ROM.GetFile(GameFile.MoveShop).FilePath; + var result = FlatDumper.GetTable(file!); + File.WriteAllText(GetPath("MoveShop.csv"), result); + } + } +} diff --git a/pkNX.WinForms/Dumping/GameDumperSWSH.cs b/pkNX.WinForms/Dumping/GameDumperSWSH.cs index dc6d1182..e389d53a 100644 --- a/pkNX.WinForms/Dumping/GameDumperSWSH.cs +++ b/pkNX.WinForms/Dumping/GameDumperSWSH.cs @@ -58,7 +58,7 @@ public void DumpPokeInfo() var pt = ROM.Data.PersonalData; var altForms = pt.GetFormList(s, pt.MaxSpeciesID); - var entryNames = pt.GetPersonalEntryList(altForms, s, pt.MaxSpeciesID, out var _, out _); + var entryNames = pt.GetPersonalEntryList(altForms, s, pt.MaxSpeciesID, out _, out _); var moveNames = ROM.GetStrings(TextName.MoveNames); var pd = new PersonalDumperSWSH diff --git a/pkNX.WinForms/Dumping/PersonalDumperPLA.cs b/pkNX.WinForms/Dumping/PersonalDumperPLA.cs new file mode 100644 index 00000000..ea6d632b --- /dev/null +++ b/pkNX.WinForms/Dumping/PersonalDumperPLA.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using pkNX.Structures; +using pkNX.Structures.FlatBuffers; +#nullable disable // meh + +namespace pkNX.WinForms +{ + public class PersonalDumperPLA + { + private void AddTRs(List lines, PersonalInfo pi, string specCode) + { + if (!(TMIndexes?.Count > 0)) + return; + var tmhm = pi.TMHM; + int count = 0; + lines.Add("Legacy TRs:"); + for (int i = 0; i < 100; i++) + { + if (!tmhm[100 + i]) + continue; + var move = TMIndexes[100 + i]; + lines.Add($"- [TR{i:00}] {Moves[move]}"); + count++; + + MoveSpeciesLearn[move].Add(specCode); + } + if (count == 0) + lines.Add("None!"); + } + + public bool HasAbilities { get; set; } = true; + public bool HasItems { get; set; } = true; + + public IReadOnlyList Abilities { private get; set; } + public IReadOnlyList Types { private get; set; } + public IReadOnlyList Items { private get; set; } + public IReadOnlyList Colors { private get; set; } + public IReadOnlyList EggGroups { private get; set; } + public IReadOnlyList ExpGroups { private get; set; } + public IReadOnlyList EntryNames { private get; set; } + public IReadOnlyList Moves { protected get; set; } + public IReadOnlyList Species { private get; set; } + public IReadOnlyList ZukanA { private get; set; } + public IReadOnlyList ZukanB { private get; set; } + + public Learnset8aMeta[] EntryLearnsets { private get; set; } + public IReadOnlyList EntryEggMoves { private get; set; } + public EvolutionSet8a[] Evos { private get; set; } + public IReadOnlyList TMIndexes { protected get; set; } + + private static readonly string[] AbilitySuffix = { " (1)", " (2)", " (H)" }; + private static readonly string[] ItemPrefix = { "Item 1 (50%)", "Item 2 (5%)", "Item 3 (1%)" }; + + public IReadOnlyList> MoveSpeciesLearn { get; private set; } + + public PersonalDumperSettings Settings = new(); + + public List Dump(PersonalTable table) + { + var lines = new List(); + var ml = new List[Moves.Count]; + for (int i = 0; i < ml.Length; i++) + ml[i] = new List(); + MoveSpeciesLearn = ml; + + for (int species = 0; species <= table.MaxSpeciesID; species++) + { + var spec = table[species]; + for (int form = 0; form < spec.FormeCount; form++) + AddDump(lines, table, species, form); + } + return lines; + } + + public void AddDump(List lines, PersonalTable table, int species, int form) + { + var index = table.GetFormeIndex(species, form); + var entry = table[index]; + string name = EntryNames[index]; + AddDump(lines, entry, index, name, species, form); + lines.Add(""); + } + + private void AddDump(List lines, PersonalInfo pi, int entry, string name, int species, int form) + { + if (pi is PersonalInfoSWSH { IsPresentInGame: false }) + return; + + var specCode = pi.FormeCount > 1 ? $"{Species[species]}-{form}" : $"{Species[species]}"; + + if (Settings.Stats) + AddPersonalLines(lines, pi, entry, name, specCode); + if (Settings.Learn) + { + AddLearnsets(lines, specCode, species, form); + AddLearnsetsLegacy(lines, specCode, species, form); + } + if (Settings.TMHM) + AddTMs(lines, pi, specCode); + if (Settings.Tutor) + AddArmorTutors(lines, pi, specCode); + if (Settings.Evo) + AddEvolutions(lines, species, form); + if (Settings.Dex) + AddZukan(lines, entry); + } + + private void AddZukan(List lines, int entry) + { + if (entry >= Species.Count) + return; + lines.Add(ZukanA[entry].Replace("\\n", " ")); + lines.Add(ZukanB[entry].Replace("\\n", " ")); + } + + protected virtual void AddTMs(List lines, PersonalInfo pi, string SpecCode) + { + var tmhm = pi.TMHM; + int count = 0; + lines.Add("Legacy TMs:"); + for (int i = 0; i < 100; i++) + { + if (!tmhm[i]) + continue; + var move = TMIndexes[i]; + lines.Add($"- [TM{i:00}] {Moves[move]}"); + count++; + + MoveSpeciesLearn[move].Add(SpecCode); + } + if (count == 0) + lines.Add("None!"); + + AddTRs(lines, pi, SpecCode); + } + + protected virtual void AddArmorTutors(List lines, PersonalInfo pi, string SpecCode) + { + var shop = pi.SpecialTutors[0]; + int count = 0; + lines.Add("Move Shop:"); + for (int i = 0; i < Math.Min(shop.Length, Legal.MoveShop8a.Length); i++) + { + if (!shop[i]) + continue; + var move = Legal.MoveShop8a[i]; + lines.Add($"- {Moves[move]}"); + count++; + + MoveSpeciesLearn[move].Add(SpecCode); + } + if (count == 0) + lines.Add("None!"); + } + + private void AddLearnsetsLegacy(List lines, string specCode, int species, int form) + { + var learn = Array.Find(EntryLearnsets, z => z.Species == species && z.Form == form); + if (learn is null) + return; + + + lines.Add("Legacy Level Up Moves:"); + foreach (var x in learn.Mainline) + { + var move = x.Move; + var level = x.Level; + lines.Add($"- [{level:00}] {Moves[move]}"); + MoveSpeciesLearn[move].Add(specCode); + } + } + + private void AddLearnsets(List lines, string specCode, int species, int form) + { + var learn = Array.Find(EntryLearnsets, z => z.Species == species && z.Form == form); + if (learn is null) + return; + + lines.Add("Level Up Moves:"); + foreach (var x in learn.Arceus) + { + var move = x.Move; + var level = x.Level; + var master = x.LevelMaster; + lines.Add($"- [{level:00}] [{master:00}] {Moves[move]}"); + MoveSpeciesLearn[move].Add(specCode); + } + } + + private void AddEvolutions(List lines, int species, int form) + { + var evo = Array.Find(Evos, z => z.Index == species && z.Form == form); + if (evo?.Table is null) + return; + var evo2 = evo.Table.Where(z => z.Species != 0).ToArray(); + if (evo2.Length == 0) + return; + + var msg = evo2.Select(z => $"Evolves into {Species[z.Species]}-{z.Form} @ {z.Level} ({z.Method}) [{z.Argument}]"); + lines.AddRange(msg); + } + + private void AddPersonalLines(List lines, PersonalInfo pi, int entry, string name, string specCode) + { + Debug.WriteLine($"Dumping {specCode}"); + lines.Add("======"); + lines.Add($"{entry:000} - {name} (Stage: {pi.EvoStage})"); + lines.Add("======"); + if (pi is PersonalInfoLA { IsPresentInGame: false }) + lines.Add("Present: No"); + lines.Add($"Base Stats: {pi.HP}.{pi.ATK}.{pi.DEF}.{pi.SPA}.{pi.SPD}.{pi.SPE} (BST: {pi.BST})"); + lines.Add($"EV Yield: {pi.EV_HP}.{pi.EV_ATK}.{pi.EV_DEF}.{pi.EV_SPA}.{pi.EV_SPD}.{pi.EV_SPE}"); + lines.Add($"Gender Ratio: {pi.Gender}"); + lines.Add($"Catch Rate: {pi.CatchRate}"); + + if (HasAbilities) + { + var abils = pi.Abilities; + var msg = string.Join(" | ", abils.Select((z, j) => Abilities[z] + AbilitySuffix[j])); + lines.Add($"Abilities: {msg}"); + } + + lines.Add(string.Format(pi.Type1 != pi.Type2 + ? "Type: {0} / {1}" + : "Type: {0}", Types[pi.Type1], Types[pi.Type2])); + + if (HasItems) + { + var items = pi.Items; + if (items.Distinct().Count() == 1) + lines.Add($"Items: {Items[pi.Items[0]]}"); + else + lines.AddRange(items.Select((z, j) => $"{ItemPrefix[j]}: {Items[z]}")); + } + + lines.Add($"EXP Group: {ExpGroups[pi.EXPGrowth]}"); + lines.Add(string.Format(pi.EggGroup1 != pi.EggGroup2 + ? "Egg Group: {0} / {1}" + : "Egg Group: {0}", EggGroups[pi.EggGroup1], EggGroups[pi.EggGroup2])); + lines.Add($"Hatch Cycles: {pi.HatchCycles}"); + lines.Add($"Height: {(decimal)pi.Height / 100:00.00}m, Weight: {(decimal)pi.Weight / 10:000.0}kg, Color: {Colors[pi.Color]}"); + } + } +} \ No newline at end of file diff --git a/pkNX.WinForms/MainEditor/EditorPLA.cs b/pkNX.WinForms/MainEditor/EditorPLA.cs new file mode 100644 index 00000000..c4c2a74e --- /dev/null +++ b/pkNX.WinForms/MainEditor/EditorPLA.cs @@ -0,0 +1,129 @@ +using System.IO; +using System.Linq; +using System.Windows.Forms; +using pkNX.Containers; +using pkNX.Game; +using pkNX.Structures; +using pkNX.Structures.FlatBuffers; +using pkNX.WinForms.Subforms; + +namespace pkNX.WinForms.Controls; + +internal class EditorPLA : EditorBase +{ + protected internal EditorPLA(GameManager rom) : base(rom) { } + + public void EditCommon() + { + var text = ROM.GetFilteredFolder(GameFile.GameText, z => Path.GetExtension(z) == ".dat"); + var config = new TextConfig(ROM.Game); + var tc = new TextContainer(text, config); + using var form = new TextEditor(tc, TextEditor.TextEditorMode.Common); + form.ShowDialog(); + if (!form.Modified) + text.CancelEdits(); + } + + public void EditScript() + { + var text = ROM.GetFilteredFolder(GameFile.StoryText, z => Path.GetExtension(z) == ".dat"); + var config = new TextConfig(ROM.Game); + var tc = new TextContainer(text, config); + using var form = new TextEditor(tc, TextEditor.TextEditorMode.Script); + form.ShowDialog(); + if (!form.Modified) + text.CancelEdits(); + } + + public void EditTrainers() + { + var folder = ROM.GetFilteredFolder(GameFile.TrainerData).FilePath; + var files = Directory.GetFiles(folder); + var data = files.Select(FlatBufferConverter.DeserializeFrom).ToArray(); + var names = files.Select(Path.GetFileNameWithoutExtension).ToArray(); + var cache = new DataCache(data); + using var form = new GenericEditor(cache, names, "Trainers", canSave: false); + form.ShowDialog(); + } + + public void NotWorking_EditItems() + { + var obj = ROM.GetFilteredFolder(GameFile.ItemStats, z => new FileInfo(z).Length == 36); + var cache = new DataCache(obj) + { + Create = Item.FromBytes, + Write = item => item.Write(), + }; + using var form = new GenericEditor(cache, ROM.GetStrings(TextName.ItemNames), "Item Editor"); + form.ShowDialog(); + if (!form.Modified) + cache.CancelEdits(); + else + cache.Save(); + } + + public void EditSpawns() + { + var residentpak = ROM.GetFile(GameFile.Resident)[0]; + var resident = new GFPack(residentpak); + using var form = new MapViewer8a(ROM, resident); + form.ShowDialog(); + } + + public void EditMoves() + { + var obj = ROM[GameFile.MoveStats]; // folder + var cache = new DataCache(obj) + { + Create = FlatBufferConverter.DeserializeFrom, + Write = FlatBufferConverter.SerializeFrom, + }; + using var form = new GenericEditor(cache, ROM.GetStrings(TextName.MoveNames), "Move Editor"); + form.ShowDialog(); + if (!form.Modified) + { + cache.CancelEdits(); + return; + } + + cache.Save(); + ROM.Data.MoveData.ClearAll(); // force reload if used again + } + + public void EditItems() + { + var obj = ROM[GameFile.ItemStats]; // mini + var data = obj[0]; + var items = Item8a.GetArray(data); + var cache = new DataCache(items); + using var form = new GenericEditor(cache, ROM.GetStrings(TextName.ItemNames), "Item Editor"); + form.ShowDialog(); + if (!form.Modified) + { + cache.CancelEdits(); + return; + } + + obj[0] = Item8a.SetArray(items, data); + } + + public void EditSymbolBehave() + { + var obj = ROM.GetFile(GameFile.SymbolBehave); + var data = obj[0]; + var root = FlatBufferConverter.DeserializeFrom(data); + var cache = new DataCache(root.Table); + var names = root.Table.Select(z => $"{z.Species}{(z.Form != 0 ? $"-{z.Form}" : "")}").ToArray(); + using var form = new GenericEditor(cache, names, "Symbol Behavior Editor", canSave: false); + form.ShowDialog(); + if (!form.Modified) + return; + obj[0] = FlatBufferConverter.SerializeFrom(root); + } + + public void EditMasterDump() + { + using var md = new DumperPLA((GameManagerPLA)ROM); + md.ShowDialog(); + } +} diff --git a/pkNX.WinForms/MainEditor/EditorProvider.cs b/pkNX.WinForms/MainEditor/EditorProvider.cs index 2d3f12d9..44d50630 100644 --- a/pkNX.WinForms/MainEditor/EditorProvider.cs +++ b/pkNX.WinForms/MainEditor/EditorProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Reflection; using System.Windows.Forms; using pkNX.Game; @@ -28,15 +29,26 @@ public IEnumerable