mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-09-30 17:18:00 -05:00
Enhance Gen5 download content I/O
Initially just C-Gear, can do better! Adds i/o for Pokedex skins, Musicals, Battle Videos, Movies & PWT (B2W2), and a research-only tab for Battle Test (never distributed by GameFreak, so doesn't work).
This commit is contained in:
@@ -31,7 +31,7 @@ public static int LoadString(ReadOnlySpan<byte> data, Span<char> result)
|
||||
for (; i < data.Length; i += 2)
|
||||
{
|
||||
var value = ReadUInt16LittleEndian(data[i..]);
|
||||
if (value == Terminator)
|
||||
if (value is Terminator or 0)
|
||||
break;
|
||||
result[ctr++] = StringConverter4Util.NormalizeGenderSymbol((char)value);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ public interface ISaveBlock5BW
|
||||
Daycare5 Daycare { get; }
|
||||
BoxLayout5 BoxLayout { get; }
|
||||
PlayerData5 PlayerData { get; }
|
||||
UnityTower5 UnityTower { get; }
|
||||
SkinInfo5 SkinInfo { get; }
|
||||
BattleSubwayPlay5 BattleSubwayPlay { get; }
|
||||
BattleSubway5 BattleSubway { get; }
|
||||
Entralink5 Entralink { get; }
|
||||
|
||||
@@ -74,7 +74,7 @@ public sealed class SaveBlockAccessor5B2W2(SAV5B2W2 sav)
|
||||
new(0x22A00, 0x0850, 0x23252, 0x25F78), // 60 Entralink Forest pokémon data
|
||||
new(0x23300, 0x0284, 0x23586, 0x25F7A), // 61 Answered Questions
|
||||
new(0x23600, 0x0010, 0x23612, 0x25F7C), // 62 Unity Tower
|
||||
new(0x23700, 0x00a8, 0x237AA, 0x25F7E), // 63 PWT related data
|
||||
new(0x23700, 0x00a8, 0x237AA, 0x25F7E), // 63 Battle Institute & PWT related data
|
||||
new(0x23800, 0x016c, 0x2396E, 0x25F80), // 64 ???
|
||||
new(0x23A00, 0x0080, 0x23A82, 0x25F82), // 65 ???
|
||||
new(0x23B00, 0x00fc, 0x23BFE, 0x25F84), // 66 Hollow/Rival Block
|
||||
@@ -90,9 +90,10 @@ public sealed class SaveBlockAccessor5B2W2(SAV5B2W2 sav)
|
||||
public IReadOnlyList<BlockInfoNDS> BlockInfo => BlocksB2W2;
|
||||
public BoxLayout5 BoxLayout { get; } = new(sav, Block(sav, 0));
|
||||
public MyItem5B2W2 Items { get; } = new(sav, Block(sav, 25));
|
||||
public PlayerData5 PlayerData { get; } = new(sav, Block(sav, 27));
|
||||
public PlayerData5B2W2 PlayerData { get; } = new(sav, Block(sav, 27));
|
||||
public PlayerPosition5 PlayerPosition { get; } = new(sav, Block(sav, 28));
|
||||
public UnityTower5 UnityTower { get; } = new(sav, Block(sav, 29));
|
||||
public SkinInfo5B2W2 SkinInfo { get; } = new(sav, Block(sav, 32));
|
||||
public MysteryBlock5 Mystery { get; } = new(sav, Block(sav, 34));
|
||||
public GlobalLink5 GlobalLink { get; } = new(sav, Block(sav, 35));
|
||||
public Chatter5 Chatter { get; } = new(sav, Block(sav, 36));
|
||||
@@ -120,6 +121,8 @@ public sealed class SaveBlockAccessor5B2W2(SAV5B2W2 sav)
|
||||
MyItem ISaveBlock5BW.Items => Items;
|
||||
Entralink5 ISaveBlock5BW.Entralink => Entralink;
|
||||
Misc5 ISaveBlock5BW.Misc => Misc;
|
||||
SkinInfo5 ISaveBlock5BW.SkinInfo => SkinInfo;
|
||||
PlayerData5 ISaveBlock5BW.PlayerData => PlayerData;
|
||||
|
||||
public static Memory<byte> Block(SAV5B2W2 sav, int index)
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@ public sealed class SaveBlockAccessor5BW(SAV5BW sav) : ISaveBlockAccessor<BlockI
|
||||
new(0x23500, 0x0028, 0x2352A, 0x23F7C), // 62 ???
|
||||
new(0x23600, 0x0284, 0x23886, 0x23F7E), // 63 Answered Questions
|
||||
new(0x23900, 0x0010, 0x23912, 0x23F80), // 64 Unity Tower
|
||||
new(0x23A00, 0x005C, 0x23A5E, 0x23F82), // 65 ???
|
||||
new(0x23A00, 0x005C, 0x23A5E, 0x23F82), // 65 Battle Institute
|
||||
new(0x23B00, 0x016C, 0x23C6E, 0x23F84), // 66 ???
|
||||
new(0x23D00, 0x0040, 0x23D42, 0x23F86), // 67 ???
|
||||
new(0x23E00, 0x00FC, 0x23EFE, 0x23F88), // 68 ???
|
||||
@@ -85,9 +85,10 @@ public sealed class SaveBlockAccessor5BW(SAV5BW sav) : ISaveBlockAccessor<BlockI
|
||||
public IReadOnlyList<BlockInfoNDS> BlockInfo => BlocksBW;
|
||||
public BoxLayout5 BoxLayout { get; } = new(sav, Block(sav, 0));
|
||||
public MyItem5BW Items { get; } = new(sav, Block(sav, 25));
|
||||
public PlayerData5 PlayerData { get; } = new(sav, Block(sav, 27));
|
||||
public PlayerData5BW PlayerData { get; } = new(sav, Block(sav, 27));
|
||||
public PlayerPosition5 PlayerPosition { get; } = new(sav, Block(sav, 28));
|
||||
public UnityTower5 UnityTower { get; } = new(sav, Block(sav, 29));
|
||||
public SkinInfo5BW SkinInfo { get; } = new(sav, Block(sav, 32));
|
||||
public MysteryBlock5 Mystery { get; } = new(sav, Block(sav, 34));
|
||||
public GlobalLink5 GlobalLink { get; } = new(sav, Block(sav, 35));
|
||||
public Chatter5 Chatter { get; } = new(sav, Block(sav, 36));
|
||||
@@ -111,6 +112,8 @@ public sealed class SaveBlockAccessor5BW(SAV5BW sav) : ISaveBlockAccessor<BlockI
|
||||
MyItem ISaveBlock5BW.Items => Items;
|
||||
Entralink5 ISaveBlock5BW.Entralink => Entralink;
|
||||
Misc5 ISaveBlock5BW.Misc => Misc;
|
||||
SkinInfo5 ISaveBlock5BW.SkinInfo => SkinInfo;
|
||||
PlayerData5 ISaveBlock5BW.PlayerData => PlayerData;
|
||||
|
||||
public static Memory<byte> Block(SAV5BW sav, int index)
|
||||
{
|
||||
|
||||
@@ -57,8 +57,6 @@ private void Initialize()
|
||||
public override bool ChecksumsValid => AllBlocks.GetChecksumsValid(Data);
|
||||
public override string ChecksumInfo => AllBlocks.GetChecksumInfo(Data);
|
||||
|
||||
private int CGearInfoOffset => AllBlocks[32].Offset; // 0x1C000 - Options / Skin Info
|
||||
protected abstract int CGearDataOffset { get; } // extdata
|
||||
public sealed override bool HasPokeDex => true;
|
||||
|
||||
// Daycare
|
||||
@@ -125,44 +123,6 @@ public sealed override int LoadString(ReadOnlySpan<byte> data, Span<char> result
|
||||
public sealed override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
|
||||
=> StringConverter5.SetString(destBuffer, value, maxLength, Language, option);
|
||||
|
||||
// DLC
|
||||
private int CGearSkinInfoOffset => CGearInfoOffset + (this is SAV5B2W2 ? 0x10 : 0) + 0x24;
|
||||
|
||||
private bool CGearSkinPresent
|
||||
{
|
||||
get => Data[CGearSkinInfoOffset + 2] == 1;
|
||||
set => Data[CGearSkinInfoOffset + 2] = PlayerData.Data[this is SAV5B2W2 ? 0x6C : 0x54] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> DLCFooter => [ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 ];
|
||||
|
||||
public Memory<byte> CGearSkinData => Data.AsMemory(CGearDataOffset, CGearBackground.SIZE);
|
||||
|
||||
public void SetCGearSkin(ReadOnlySpan<byte> value)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, CGearBackground.SIZE);
|
||||
SetData(value, CGearDataOffset);
|
||||
|
||||
ushort chk = Checksums.CRC16_CCITT(value);
|
||||
var footer = Data.AsSpan(CGearDataOffset + value.Length);
|
||||
|
||||
WriteUInt16LittleEndian(footer, 1); // block updated once
|
||||
WriteUInt16LittleEndian(footer[2..], chk); // checksum
|
||||
WriteUInt16LittleEndian(footer[0x100..], chk); // second checksum
|
||||
|
||||
DLCFooter.CopyTo(footer[0x102..]);
|
||||
|
||||
ushort skinchkval = Checksums.CRC16_CCITT(footer[0x100..0x104]);
|
||||
WriteUInt16LittleEndian(footer[0x112..], skinchkval);
|
||||
|
||||
// Indicate in the save file that data is present
|
||||
WriteUInt16LittleEndian(Data.AsSpan(0x19438), 0xC21E);
|
||||
|
||||
WriteUInt16LittleEndian(Data.AsSpan(CGearSkinInfoOffset), chk);
|
||||
CGearSkinPresent = true;
|
||||
State.Edited = true;
|
||||
}
|
||||
|
||||
public abstract IReadOnlyList<BlockInfo> AllBlocks { get; }
|
||||
public abstract MyItem Items { get; }
|
||||
public abstract Zukan5 Zukan { get; }
|
||||
@@ -179,6 +139,7 @@ public void SetCGearSkin(ReadOnlySpan<byte> value)
|
||||
public abstract Musical5 Musical { get; }
|
||||
public abstract Encount5 Encount { get; }
|
||||
public abstract UnityTower5 UnityTower { get; }
|
||||
public abstract SkinInfo5 SkinInfo { get; }
|
||||
public abstract EventWork5 EventWork { get; }
|
||||
public abstract BattleBox5 BattleBox { get; }
|
||||
public abstract EntreeForest EntreeForest { get; }
|
||||
@@ -189,11 +150,6 @@ public void SetCGearSkin(ReadOnlySpan<byte> value)
|
||||
public abstract Record5 Records { get; }
|
||||
IEventFlag37 IEventFlagProvider37.EventWork => EventWork;
|
||||
|
||||
public abstract Memory<byte> BattleVideoNative { get; }
|
||||
public abstract Memory<byte> BattleVideoDownload1 { get; }
|
||||
public abstract Memory<byte> BattleVideoDownload2 { get; }
|
||||
public abstract Memory<byte> BattleVideoDownload3 { get; }
|
||||
|
||||
protected override byte[] GetFinalData()
|
||||
{
|
||||
EntreeForest.EndAccess();
|
||||
@@ -212,4 +168,143 @@ public MailDetail GetMail(int mailIndex)
|
||||
}
|
||||
|
||||
IMysteryGiftStorage IMysteryGiftStorageProvider.MysteryGiftStorage => Mystery;
|
||||
protected abstract int ExtBattleVideoNativeOffset { get; }
|
||||
protected abstract int ExtBattleVideoDownload1Offset { get; }
|
||||
protected abstract int ExtBattleVideoDownload2Offset { get; }
|
||||
protected abstract int ExtBattleVideoDownload3Offset { get; }
|
||||
protected abstract int ExtCGearOffset { get; }
|
||||
protected abstract int ExtBattleTestOffset { get; }
|
||||
protected abstract int ExtMusicalDownloadOffset { get; }
|
||||
protected abstract int ExtPokeDexSkinOffset { get; }
|
||||
protected abstract int ExtHallOfFame1Offset { get; }
|
||||
protected abstract int ExtHallOfFame2Offset { get; }
|
||||
protected abstract int ExtLink1Offset { get; }
|
||||
protected abstract int ExtLink2Offset { get; }
|
||||
|
||||
/// <summary> Variable sized NARC download depending on the game (B/W vs B2/W2). </summary>
|
||||
public abstract int MusicalDownloadSize { get; }
|
||||
public const int HallOfFameSize = 0x155C;
|
||||
private const int Link3DSDataSize = 0x400;
|
||||
|
||||
public Memory<byte> BattleVideoNative => Data.AsMemory(ExtBattleVideoNativeOffset, BattleVideo5.SIZE);
|
||||
public Memory<byte> BattleVideoDownload1 => Data.AsMemory(ExtBattleVideoDownload1Offset, BattleVideo5.SIZE);
|
||||
public Memory<byte> BattleVideoDownload2 => Data.AsMemory(ExtBattleVideoDownload2Offset, BattleVideo5.SIZE);
|
||||
public Memory<byte> BattleVideoDownload3 => Data.AsMemory(ExtBattleVideoDownload3Offset, BattleVideo5.SIZE);
|
||||
public Memory<byte> CGearSkinData => Data.AsMemory(ExtCGearOffset, CGearBackground.SIZE);
|
||||
public Memory<byte> BattleTest => Data.AsMemory(ExtBattleTestOffset, BattleTest5.SIZE);
|
||||
public Memory<byte> MusicalDownloadData => Data.AsMemory(ExtMusicalDownloadOffset, MusicalDownloadSize);
|
||||
public Memory<byte> PokedexSkinData => Data.AsMemory(ExtPokeDexSkinOffset, PokeDexSkin5.SIZE);
|
||||
public Memory<byte> HallOfFame1 => Data.AsMemory(ExtHallOfFame1Offset, HallOfFameSize);
|
||||
public Memory<byte> HallOfFame2 => Data.AsMemory(ExtHallOfFame2Offset, HallOfFameSize);
|
||||
public Memory<byte> Link1Data => Data.AsMemory(ExtLink1Offset, Link3DSDataSize);
|
||||
public Memory<byte> Link2Data => Data.AsMemory(ExtLink2Offset, Link3DSDataSize);
|
||||
|
||||
private const int ExtFooterLength = 0x14;
|
||||
|
||||
/// <summary>
|
||||
/// Writes an extdata section
|
||||
/// </summary>
|
||||
/// <param name="data">Section of data to write</param>
|
||||
/// <param name="offset">Offset within the save file to write to</param>
|
||||
/// <param name="size">Expected size of the data</param>
|
||||
/// <param name="count">Update count</param>
|
||||
/// <returns>Checksum of the <see cref="data"/></returns>
|
||||
protected ushort WriteExtSection(ReadOnlySpan<byte> data, int offset, int size, ushort count)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(data.Length, size);
|
||||
SetData(data, offset);
|
||||
|
||||
// Update Tail Section
|
||||
ushort chk = Checksums.CRC16_CCITT(data);
|
||||
var tail = Data.AsSpan(offset + size);
|
||||
WriteUInt16LittleEndian(tail, count); // block updated counter
|
||||
WriteUInt16LittleEndian(tail[2..], chk); // checksum
|
||||
|
||||
// Update Footer
|
||||
int lengthInner = size + 0x100 - (size % 0x100); // wasting 0x100 bytes, nice!
|
||||
var footer = Data.AsSpan(offset + lengthInner, ExtFooterLength);
|
||||
WriteFooterDLC(footer, chk, lengthInner + ExtFooterLength, count);
|
||||
return chk;
|
||||
}
|
||||
|
||||
private static void WriteFooterDLC(Span<byte> data, ushort chk, int length, ushort count)
|
||||
{
|
||||
WriteInt32LittleEndian(data, chk);
|
||||
chk = Checksums.CRC16_CCITT(data[..4]); // checksum of a checksum
|
||||
WriteFooter51(data[4..], chk, length, count);
|
||||
}
|
||||
|
||||
private static void WriteFooter51(Span<byte> data, ushort chk, int length, ushort count)
|
||||
{
|
||||
WriteInt32LittleEndian(data, count);
|
||||
WriteInt32LittleEndian(data[0x04..], length);
|
||||
WriteInt32LittleEndian(data[0x08..], 0x31053527); // '5.1 magic prime 822424871
|
||||
|
||||
WriteUInt16LittleEndian(data[0x0C..], 0);
|
||||
WriteUInt16LittleEndian(data[0x0E..], chk);
|
||||
}
|
||||
|
||||
public void SetBattleVideo(int index, ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.BattleVideo0 + index, count);
|
||||
var offset = index switch
|
||||
{
|
||||
0 => ExtBattleVideoNativeOffset,
|
||||
1 => ExtBattleVideoDownload1Offset,
|
||||
2 => ExtBattleVideoDownload2Offset,
|
||||
3 => ExtBattleVideoDownload3Offset,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
WriteExtSection(data, offset, BattleVideo5.SIZE, count);
|
||||
}
|
||||
|
||||
public Memory<byte> GetBattleVideo(int index) => index switch
|
||||
{
|
||||
0 => BattleVideoNative,
|
||||
1 => BattleVideoDownload1,
|
||||
2 => BattleVideoDownload2,
|
||||
3 => BattleVideoDownload3,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
|
||||
public void SetCGearSkin(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
var chk = WriteExtSection(data, ExtCGearOffset, CGearBackground.SIZE, count);
|
||||
|
||||
// Indicate in the save file that data is present
|
||||
SkinInfo.CGearSkinChecksum = chk;
|
||||
SkinInfo.HasCGearSkin = true;
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.CGearSkin, count);
|
||||
}
|
||||
|
||||
public void SetBattleTest(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
WriteExtSection(data, ExtBattleTestOffset, BattleTest5.SIZE, count);
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.BattleTest, count);
|
||||
}
|
||||
|
||||
public void SetMusical(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
WriteExtSection(data, ExtMusicalDownloadOffset, MusicalDownloadSize, count);
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.Musical, count);
|
||||
Musical.IsAvailableMusicalDLC = true;
|
||||
}
|
||||
|
||||
public void SetPokeDexSkin(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
WriteExtSection(data, ExtPokeDexSkinOffset, PokeDexSkin5.SIZE, count);
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.PokedexSkin, count);
|
||||
}
|
||||
|
||||
public void SetHallOfFame(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
WriteExtSection(data, ExtHallOfFame1Offset, HallOfFameSize, count);
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.HallOfFame, count);
|
||||
}
|
||||
|
||||
public void SetLink1Data(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
=> WriteExtSection(data, ExtLink1Offset, Link3DSDataSize, count);
|
||||
|
||||
public void SetLink2Data(ReadOnlySpan<byte> data, ushort count = 1)
|
||||
=> WriteExtSection(data, ExtLink2Offset, Link3DSDataSize, count);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -25,7 +26,7 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
|
||||
public override Chatter5 Chatter => Blocks.Chatter;
|
||||
public override Daycare5 Daycare => Blocks.Daycare;
|
||||
public override BoxLayout5 BoxLayout => Blocks.BoxLayout;
|
||||
public override PlayerData5 PlayerData => Blocks.PlayerData;
|
||||
public override PlayerData5B2W2 PlayerData => Blocks.PlayerData;
|
||||
public override PlayerPosition5 PlayerPosition => Blocks.PlayerPosition;
|
||||
public override BattleSubwayPlay5 BattleSubwayPlay => Blocks.BattleSubwayPlay;
|
||||
public override BattleSubway5 BattleSubway => Blocks.BattleSubway;
|
||||
@@ -33,6 +34,7 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
|
||||
public override Musical5 Musical => Blocks.Musical;
|
||||
public override Encount5 Encount => Blocks.Encount;
|
||||
public override UnityTower5 UnityTower => Blocks.UnityTower;
|
||||
public override SkinInfo5 SkinInfo => Blocks.SkinInfo;
|
||||
public override EventWork5B2W2 EventWork => Blocks.EventWork;
|
||||
public override BattleBox5 BattleBox => Blocks.BattleBox;
|
||||
public override EntreeForest EntreeForest => Blocks.EntreeForest;
|
||||
@@ -59,9 +61,65 @@ public Span<byte> RivalTrash
|
||||
set { if (value.Length == MaxStringLengthTrainer * 2) value.CopyTo(Data.AsSpan(0x23BA4)); }
|
||||
}
|
||||
|
||||
public override Memory<byte> BattleVideoNative => Data.AsMemory(0x4C000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4DA00, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload2 => Data.AsMemory(0x4F400, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload3 => Data.AsMemory(0x50E00, BattleVideo5.SIZE_USED);
|
||||
protected override int CGearDataOffset => 0x52800; // ^ + 0x1A00 spacing
|
||||
protected override int ExtBattleVideoNativeOffset => 0x4C000; // 0x1A00
|
||||
protected override int ExtBattleVideoDownload1Offset => 0x4DA00; // 0x1A00
|
||||
protected override int ExtBattleVideoDownload2Offset => 0x4F400; // 0x1A00
|
||||
protected override int ExtBattleVideoDownload3Offset => 0x50E00; // 0x1A00
|
||||
protected override int ExtCGearOffset => 0x52800; // 0x2800
|
||||
protected override int ExtBattleTestOffset => 0x55000; // 0x800
|
||||
protected override int ExtMusicalDownloadOffset => 0x55800; // 0x18000
|
||||
protected override int ExtPokeDexSkinOffset => 0x6D800; // 0x6800
|
||||
protected override int ExtHallOfFame1Offset => 0x74000; // 0x1800
|
||||
protected override int ExtHallOfFame2Offset => 0x75800; // 0x1800
|
||||
|
||||
public const int PokestarOffset = 0x77000; // 0x600 * 8
|
||||
public const int PWTOffset = 0x7A000; // 0x1400 * 3
|
||||
|
||||
public const int DreamRadarOffset = 0x7DC00; // 0x400, size 0xC8 ?
|
||||
|
||||
protected override int ExtLink1Offset => 0x7E000; // 0x400
|
||||
protected override int ExtLink2Offset => 0x7E400; // 0x400
|
||||
|
||||
public override int MusicalDownloadSize => MusicalShow5.SIZE_B2W2;
|
||||
public const int ExtUnk7E800 = 0x7E800; // Key System?
|
||||
public const int ExtUnkCRGF = 0x7F000; // [0..F] => checksum @ 0x10
|
||||
|
||||
// B2/W2 have Pokéstar Studios and PWT DLC data too.
|
||||
public const int PokestarCount = 8;
|
||||
public const int PokestarLength = PokestarMovie5.SIZE;
|
||||
public const int PokestarInterval = 0x600;
|
||||
private static int GetPokestarOffset(int index) => PokestarOffset + (index * PokestarInterval);
|
||||
|
||||
public Memory<byte> GetPokestarMovie([Range(0, PokestarCount)] int index)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, PokestarCount, nameof(index));
|
||||
return Data.AsMemory(GetPokestarOffset(index), PokestarLength);
|
||||
}
|
||||
|
||||
public void SetPokestarMovie([Range(0, PokestarCount)] int index, ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, PokestarCount, nameof(index));
|
||||
var offset = GetPokestarOffset(index);
|
||||
WriteExtSection(data, offset, PokestarLength, count);
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.Movie1 + index, count);
|
||||
}
|
||||
|
||||
public const int PWTCount = 3;
|
||||
public const int PWTLength = 0x1214;
|
||||
public const int PWTInterval = 0x1400;
|
||||
private static int GetPWTOffset(int index) => PWTOffset + (index * PWTInterval);
|
||||
|
||||
public Memory<byte> GetPWT([Range(0, PWTCount)] int index)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, PWTCount, nameof(index));
|
||||
return Data.AsMemory(PWTOffset + (index * PWTInterval), PWTLength);
|
||||
}
|
||||
|
||||
public void SetPWT([Range(0, PWTCount)] int index, ReadOnlySpan<byte> data, ushort count = 1)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, PWTCount, nameof(index));
|
||||
var offset = GetPWTOffset(index);
|
||||
WriteExtSection(data, offset, PWTLength, count);
|
||||
PlayerData.UpdateExtData(ExtDataSectionNote5.PWT1 + index, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -33,6 +32,7 @@ public sealed class SAV5BW : SAV5
|
||||
public override Musical5 Musical => Blocks.Musical;
|
||||
public override Encount5BW Encount => Blocks.Encount;
|
||||
public override UnityTower5 UnityTower => Blocks.UnityTower;
|
||||
public override SkinInfo5 SkinInfo => Blocks.SkinInfo;
|
||||
public override EventWork5BW EventWork => Blocks.EventWork;
|
||||
public override BattleBox5 BattleBox => Blocks.BattleBox;
|
||||
public override EntreeForest EntreeForest => Blocks.EntreeForest;
|
||||
@@ -42,9 +42,19 @@ public sealed class SAV5BW : SAV5
|
||||
public override AdventureInfo5 AdventureInfo => Blocks.AdventureInfo;
|
||||
public override Record5 Records => Blocks.Records;
|
||||
|
||||
public override Memory<byte> BattleVideoNative => Data.AsMemory(0x4A000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4C000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload2 => Data.AsMemory(0x4E000, BattleVideo5.SIZE_USED);
|
||||
public override Memory<byte> BattleVideoDownload3 => Data.AsMemory(0x50000, BattleVideo5.SIZE_USED);
|
||||
protected override int CGearDataOffset => 0x52000; // ^ + 0x2000 spacing
|
||||
protected override int ExtBattleVideoNativeOffset => 0x4A000;
|
||||
protected override int ExtBattleVideoDownload1Offset => 0x4C000; // 0x2000
|
||||
protected override int ExtBattleVideoDownload2Offset => 0x4E000; // 0x2000
|
||||
protected override int ExtBattleVideoDownload3Offset => 0x50000; // 0x2000
|
||||
protected override int ExtCGearOffset => 0x52000; // 0x3000
|
||||
protected override int ExtBattleTestOffset => 0x55000; // 0x1000
|
||||
protected override int ExtMusicalDownloadOffset => 0x56000; // 0x20000
|
||||
protected override int ExtPokeDexSkinOffset => 0x76000; // 0x6800
|
||||
protected override int ExtHallOfFame1Offset => 0x7C800; // 0x1800
|
||||
protected override int ExtHallOfFame2Offset => 0x7E000; // 0x1800
|
||||
|
||||
protected override int ExtLink1Offset => 0x7F800; // 0x400
|
||||
protected override int ExtLink2Offset => 0x7FC00; // 0x400
|
||||
|
||||
public override int MusicalDownloadSize => MusicalShow5.SIZE_BW;
|
||||
}
|
||||
|
||||
@@ -11,25 +11,21 @@ namespace PKHeX.Core;
|
||||
/// <param name="Raw">Chunk of memory storing the structure.</param>
|
||||
public sealed class BattleVideo5(Memory<byte> Raw) : IBattleVideo
|
||||
{
|
||||
private const int SIZE = 0x1900;
|
||||
private const int SIZE_FOOTER = 0x14;
|
||||
public const int SIZE_USED = SIZE + SIZE_FOOTER;
|
||||
//public const int SIZE_BLOCK = 0x2000;
|
||||
//bw is 0x2000 between each video, b2w2 is 0x1A00
|
||||
public const int SIZE = 0x18A4;
|
||||
public const string Extension = "bv5";
|
||||
|
||||
private Span<byte> Data => Raw.Span[..SIZE_USED];
|
||||
private Span<byte> Data => Raw.Span;
|
||||
public bool IsUninitialized => !Data.ContainsAnyExcept<byte>(0xFF, 0);
|
||||
|
||||
public bool IsDecrypted;
|
||||
public bool IsDecrypted { get; set; }
|
||||
private Span<byte> CryptoData => Data[0xC4..0x18A0];
|
||||
private Span<byte> Footer => Data.Slice(SIZE, SIZE_FOOTER);
|
||||
|
||||
public Span<byte> DecryptedChecksumRegion => CryptoData; // ccitt16 -> 0x18A0
|
||||
public Span<byte> EncryptedChecksumRegion => Data[..0x18A4]; // ccitt16 -> 0x18A6
|
||||
public Span<byte> FooterChecksumRegion => Footer[..4]; // ccitt16 -> 0x12
|
||||
|
||||
public byte Generation => 5;
|
||||
public IEnumerable<PKM> Contents => GetTeam(0).Concat(GetTeam(1)); // don't bother with multi-battles
|
||||
public static bool IsValid(ReadOnlySpan<byte> data) => data.Length == SIZE_USED && ReadUInt32LittleEndian(data[0x1908..]) == SIZE_USED;
|
||||
public static bool IsValid(ReadOnlySpan<byte> data) => data.Length == SIZE
|
||||
&& ReadUInt32LittleEndian(data[^2..]) == Checksums.CRC16_CCITT(data[..^4]);
|
||||
|
||||
// Structure:
|
||||
// 0xC4 - 0x18A0: encrypted region
|
||||
@@ -42,26 +38,88 @@ public sealed class BattleVideo5(Memory<byte> Raw) : IBattleVideo
|
||||
// 0x0FA0: u16 count Max, u16 count Present, (6 * sizeof(0x70)) pokemon -- 0x2A4
|
||||
// 0x1244: u16 count Max, u16 count Present, (6 * sizeof(0x70)) pokemon -- 0x2A4
|
||||
// 0x14E8: u16 count Max, u16 count Present, (6 * sizeof(0x70)) pokemon -- 0x2A4
|
||||
|
||||
// Assuming participant are "real" humans, and not NPCs:
|
||||
// 0x178C: 0x44 trainer data (u32 flag, char[8] OT, remainder idc)
|
||||
// 0x17D0: 0x44 trainer data (u32 flag, char[8] OT, remainder idc)
|
||||
// 0x1814: 0x44 trainer data (u32 flag, char[8] OT, remainder idc)
|
||||
// 0x1858: 0x44 trainer data (u32 flag, char[8] OT, remainder idc)
|
||||
// If a participant is an NPC, the flag is `2` and Trainer name is +0x10 from where it normally is.
|
||||
|
||||
// 0x189C: 0xC bytes ???
|
||||
|
||||
public const int SizeVideoPoke = 0x70;
|
||||
|
||||
public uint Key { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
public string VideoName
|
||||
{
|
||||
get => StringConverter5.GetString(Data[..0x10]);
|
||||
set => StringConverter5.SetString(Data[..0x10], value, 8, 0, StringConverterOption.ClearZeroSafeTerminate);
|
||||
}
|
||||
|
||||
// Other Data?
|
||||
|
||||
public byte TrainerBirthMonth { get => Data[0x15]; set => Data[0x15] = value; }
|
||||
public byte TrainerAvatar { get => Data[0x16]; set => Data[0x16] = value; }
|
||||
public byte TrainerNation { get => Data[0x17]; set => Data[0x17] = value; }
|
||||
public byte TrainerRegion { get => Data[0x18]; set => Data[0x18] = value; }
|
||||
public ushort TrainerSpecies { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], value); }
|
||||
|
||||
public ushort Team1Species1 { get => ReadUInt16LittleEndian(Data[0x80..]); set => WriteUInt16LittleEndian(Data[0x80..], value); }
|
||||
public ushort Team1Species2 { get => ReadUInt16LittleEndian(Data[0x82..]); set => WriteUInt16LittleEndian(Data[0x82..], value); }
|
||||
public ushort Team1Species3 { get => ReadUInt16LittleEndian(Data[0x84..]); set => WriteUInt16LittleEndian(Data[0x84..], value); }
|
||||
public ushort Team1Species4 { get => ReadUInt16LittleEndian(Data[0x86..]); set => WriteUInt16LittleEndian(Data[0x86..], value); }
|
||||
public ushort Team1Species5 { get => ReadUInt16LittleEndian(Data[0x88..]); set => WriteUInt16LittleEndian(Data[0x88..], value); }
|
||||
public ushort Team1Species6 { get => ReadUInt16LittleEndian(Data[0x8A..]); set => WriteUInt16LittleEndian(Data[0x8A..], value); }
|
||||
|
||||
public ushort Team2Species1 { get => ReadUInt16LittleEndian(Data[0x8C..]); set => WriteUInt16LittleEndian(Data[0x8C..], value); }
|
||||
public ushort Team2Species2 { get => ReadUInt16LittleEndian(Data[0x8E..]); set => WriteUInt16LittleEndian(Data[0x8E..], value); }
|
||||
public ushort Team2Species3 { get => ReadUInt16LittleEndian(Data[0x90..]); set => WriteUInt16LittleEndian(Data[0x90..], value); }
|
||||
public ushort Team2Species4 { get => ReadUInt16LittleEndian(Data[0x92..]); set => WriteUInt16LittleEndian(Data[0x92..], value); }
|
||||
public ushort Team2Species5 { get => ReadUInt16LittleEndian(Data[0x94..]); set => WriteUInt16LittleEndian(Data[0x94..], value); }
|
||||
public ushort Team2Species6 { get => ReadUInt16LittleEndian(Data[0x96..]); set => WriteUInt16LittleEndian(Data[0x96..], value); }
|
||||
|
||||
public ushort BattleNumber { get => ReadUInt16LittleEndian(Data[0xA4..]); set => WriteUInt16LittleEndian(Data[0xA4..], value); }
|
||||
public byte BattleMode { get => Data[0xA6]; set => Data[0xA6] = value; }
|
||||
public byte BattleType { get => Data[0xA7]; set => Data[0xA7] = value; } // Launcher
|
||||
public ushort StartSentinel { get => ReadUInt16LittleEndian(Data[0xA8..]); set => WriteUInt16LittleEndian(Data[0xA8..], value); }
|
||||
public byte Boolean { get => Data[0xAA]; set => Data[0xAA] = value; } // 0 or 1
|
||||
public byte OneHundred { get => Data[0xAB]; set => Data[0xAB] = value; } // 100
|
||||
|
||||
public ulong BattleVideoUploadId { get => ReadUInt64LittleEndian(Data[0xB8..]); set => WriteUInt64LittleEndian(Data[0xB8..], value); }
|
||||
|
||||
// Encrypted Section
|
||||
public ulong RandomSeed { get => ReadUInt64LittleEndian(Data[0xC4..]); set => WriteUInt64LittleEndian(Data[0xC4..], value); }
|
||||
public ulong RandomMult { get => ReadUInt64LittleEndian(Data[0xCC..]); set => WriteUInt64LittleEndian(Data[0xCC..], value); }
|
||||
public ulong RandomAdd { get => ReadUInt64LittleEndian(Data[0xD4..]); set => WriteUInt64LittleEndian(Data[0xD4..], value); }
|
||||
public uint BattlePad1 { get => ReadUInt32LittleEndian(Data[0xDC..]); set => WriteUInt32LittleEndian(Data[0xDC..], value); }
|
||||
public uint BattlePad2 { get => ReadUInt32LittleEndian(Data[0xE0..]); set => WriteUInt32LittleEndian(Data[0xE0..], value); }
|
||||
|
||||
private const int TeamCount = 4;
|
||||
private const int TeamLength = 0x2A4;
|
||||
public Span<byte> Team1 => Data.Slice(0x0CFC, TeamLength);
|
||||
public Span<byte> Team2 => Data.Slice(0x0FA0, TeamLength);
|
||||
public Span<byte> Team3 => Data.Slice(0x1244, TeamLength);
|
||||
public Span<byte> Team4 => Data.Slice(0x14E8, TeamLength);
|
||||
|
||||
private const int TrainerLength = 0x44;
|
||||
public Span<byte> Trainer1 => Data.Slice(0x178C, TrainerLength); // + 0x10 something else
|
||||
public Span<byte> Trainer2 => Data.Slice(0x17D0, TrainerLength);
|
||||
public Span<byte> Trainer3 => Data.Slice(0x1814, TrainerLength);
|
||||
public Span<byte> Trainer4 => Data.Slice(0x1858, TrainerLength);
|
||||
|
||||
// should always be 0xE281
|
||||
public ushort EndSentinel { get => ReadUInt16LittleEndian(Data[0x189C..]); set => WriteUInt16LittleEndian(Data[0x189C..], value); }
|
||||
public ushort Unused { get => ReadUInt16LittleEndian(Data[0x189E..]); set => WriteUInt16LittleEndian(Data[0x189E..], value); }
|
||||
|
||||
/// <summary> <see cref="CalculateDecryptedChecksum"/> </summary>
|
||||
public ushort Seed { get => ReadUInt16LittleEndian(Data[0x18A0..]); set => WriteUInt16LittleEndian(Data[0x18A0..], value); }
|
||||
// 0000: ^ seed is truncated to u16.
|
||||
public ushort EncryptedCount { get => ReadUInt16LittleEndian(Data[0x18A4..]); set => WriteUInt16LittleEndian(Data[0x18A4..], value); }
|
||||
public ushort EncryptedChecksum { get => ReadUInt16LittleEndian(Data[0x18A6..]); set => WriteUInt16LittleEndian(Data[0x18A6..], value); }
|
||||
// 0xFF padding(?) to 0x1900
|
||||
|
||||
/// <summary> Decrypts the object. If already decrypted, does nothing. </summary>
|
||||
public void Decrypt() => SetDecryptedState(true);
|
||||
/// <summary> Encrypts the object. If already encrypted, does nothing. </summary>
|
||||
public void Encrypt() => SetDecryptedState(false);
|
||||
public void SetDecryptedState(bool state)
|
||||
private void SetDecryptedState(bool state)
|
||||
{
|
||||
if (state == IsDecrypted)
|
||||
return;
|
||||
@@ -72,32 +130,12 @@ public void SetDecryptedState(bool state)
|
||||
/// <summary>
|
||||
/// Same as Gen4!
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public uint GetEncryptionSeed()
|
||||
{
|
||||
uint seed = Seed;
|
||||
return seed | (~seed << 16);
|
||||
}
|
||||
|
||||
#region Footer
|
||||
public bool SizeValid => BlockSize == SIZE;
|
||||
public bool ChecksumValid => FooterChecksum == CalculateFooterChecksum();
|
||||
|
||||
public ushort EncryptedChecksumCopy { get => ReadUInt16LittleEndian(Footer); set => WriteUInt16LittleEndian(Footer, value); }
|
||||
// 0000
|
||||
public uint FooterCount { get => ReadUInt32LittleEndian(Footer[0x4..]); set => WriteUInt32LittleEndian(Footer[0x4..], value); }
|
||||
public int BlockSize { get => ReadInt32LittleEndian(Footer[0x8..]); set => WriteInt32LittleEndian(Footer[0x8..], value); }
|
||||
public uint BlockID { get => ReadUInt32LittleEndian(Footer[0xC..]); set => WriteUInt32LittleEndian(Footer[0xC..], value); }
|
||||
// 0000
|
||||
public ushort FooterChecksum { get => ReadUInt16LittleEndian(Footer[0x12..]); set => WriteUInt16LittleEndian(Footer[0x12..], value); }
|
||||
|
||||
private ushort CalculateEncryptedChecksum()
|
||||
{
|
||||
if (IsDecrypted)
|
||||
throw new InvalidOperationException("Cannot calculate checksum when decrypted.");
|
||||
return Checksums.CRC16_CCITT(EncryptedChecksumRegion);
|
||||
}
|
||||
|
||||
private ushort CalculateDecryptedChecksum()
|
||||
{
|
||||
if (!IsDecrypted)
|
||||
@@ -105,41 +143,21 @@ private ushort CalculateDecryptedChecksum()
|
||||
return Checksums.CRC16_CCITT(DecryptedChecksumRegion);
|
||||
}
|
||||
|
||||
private ushort CalculateFooterChecksum() => Checksums.CRC16_CCITT(FooterChecksumRegion);
|
||||
|
||||
public void RefreshChecksums()
|
||||
{
|
||||
var state = IsDecrypted;
|
||||
Decrypt();
|
||||
Seed = CalculateDecryptedChecksum();
|
||||
Encrypt();
|
||||
EncryptedChecksum = CalculateEncryptedChecksum();
|
||||
FooterChecksum = CalculateFooterChecksum();
|
||||
SetDecryptedState(state);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
||||
private const int TrainerCount = 4;
|
||||
private const int TrainerLength = 0x2A4;
|
||||
public Span<byte> Trainer1 => Data.Slice(0x0CFC, TrainerLength);
|
||||
public Span<byte> Trainer2 => Data.Slice(0x0FA0, TrainerLength);
|
||||
public Span<byte> Trainer3 => Data.Slice(0x1244, TrainerLength);
|
||||
public Span<byte> Trainer4 => Data.Slice(0x14E8, TrainerLength);
|
||||
|
||||
public PK5[] GetTeam(int trainer)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<uint>((uint)trainer, TrainerCount);
|
||||
var span = trainer switch
|
||||
{
|
||||
0 => Trainer1,
|
||||
1 => Trainer2,
|
||||
2 => Trainer3,
|
||||
3 => Trainer4,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(trainer)),
|
||||
};
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<uint>((uint)trainer, TeamLength);
|
||||
var span = GetTrainer(trainer);
|
||||
|
||||
var state = IsDecrypted;
|
||||
Decrypt();
|
||||
@@ -159,6 +177,30 @@ public PK5[] GetTeam(int trainer)
|
||||
return result;
|
||||
}
|
||||
|
||||
private Span<byte> GetTrainer(int trainer) => trainer switch
|
||||
{
|
||||
0 => Trainer1,
|
||||
1 => Trainer2,
|
||||
2 => Trainer3,
|
||||
3 => Trainer4,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(trainer)),
|
||||
};
|
||||
|
||||
public string GetTrainerName(int trainer)
|
||||
{
|
||||
var state = IsDecrypted;
|
||||
Decrypt();
|
||||
var data = GetTrainer(trainer);
|
||||
uint flag = ReadUInt32LittleEndian(data);
|
||||
data = data[4..];
|
||||
if (flag != 1) // not human?
|
||||
data = data[0x10..];
|
||||
var buffer = data[..(7 * 2)];
|
||||
var result = !buffer.ContainsAnyExcept<byte>(0) ? string.Empty : StringConverter5.GetString(buffer);
|
||||
SetDecryptedState(state);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary> <see cref="BattleVideo4.InflateToPK4"/> </summary>
|
||||
public static void InflateToPK5(ReadOnlySpan<byte> video, Span<byte> entity)
|
||||
{
|
||||
@@ -235,4 +277,31 @@ private static void VerifySpanSizes(ReadOnlySpan<byte> entity, ReadOnlySpan<byte
|
||||
throw new ArgumentOutOfRangeException(nameof(entity), "Entity size is too small.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
public const ulong BWRNG_M = 0x5D588B656C078965ul;
|
||||
public const ulong BWRNG_A = 0x0000000000269EC3ul;
|
||||
|
||||
public static bool GetIsDecrypted(ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (data.Length < SIZE)
|
||||
return false;
|
||||
if (ReadUInt64LittleEndian(data[0xCC..]) != BWRNG_M) // mul
|
||||
return false;
|
||||
if (ReadUInt64LittleEndian(data[0xD4..]) != BWRNG_A) // add
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetTrainerNames()
|
||||
{
|
||||
var state = IsDecrypted;
|
||||
var tr1 = GetTrainerName(0);
|
||||
var tr2 = GetTrainerName(1);
|
||||
var tr3 = GetTrainerName(2);
|
||||
var tr4 = GetTrainerName(3);
|
||||
SetDecryptedState(state);
|
||||
if (string.IsNullOrWhiteSpace(tr3))
|
||||
return $"{tr1} vs {tr2}";
|
||||
return $"{tr1} & {tr2} vs {tr3} & {tr4}";
|
||||
}
|
||||
}
|
||||
|
||||
12
PKHeX.Core/Saves/Substructures/Gen5/BattleTest5.cs
Normal file
12
PKHeX.Core/Saves/Substructures/Gen5/BattleTest5.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class BattleTest5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE = 0x5C8;
|
||||
public const string Extension = "bt5";
|
||||
|
||||
private Span<byte> Data => Raw.Span;
|
||||
public bool IsUninitialized => !Data.ContainsAnyExcept<byte>(0xFF, 0);
|
||||
}
|
||||
@@ -4,8 +4,24 @@ namespace PKHeX.Core;
|
||||
|
||||
public sealed class Musical5(SAV5 SAV, Memory<byte> raw) : SaveBlock<SAV5>(SAV, raw)
|
||||
{
|
||||
public const int MusicalNameMaxLength = 20;
|
||||
|
||||
private Span<byte> MusicalTrash => Data.Slice(0x208, MusicalNameMaxLength * 2);
|
||||
|
||||
public string MusicalName
|
||||
{
|
||||
get => StringConverter5.GetString(MusicalTrash);
|
||||
set => StringConverter5.SetString(MusicalTrash, value, MusicalNameMaxLength, 0, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
private const int PropOffset = 0x258;
|
||||
|
||||
public bool IsAvailableMusicalDLC
|
||||
{
|
||||
get => Data[0x29E] == 1;
|
||||
set => Data[0x29E] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
public void UnlockAllMusicalProps()
|
||||
{
|
||||
// 100 props, which is 12.5 bytes of bitflags.
|
||||
@@ -17,12 +33,12 @@ public void UnlockAllMusicalProps()
|
||||
public bool GetHasProp(int prop)
|
||||
{
|
||||
var bitOffset = prop >> 3;
|
||||
return SAV.GetFlag(Data, PropOffset + bitOffset, prop & 7);
|
||||
return FlagUtil.GetFlag(Data, PropOffset + bitOffset, prop & 7);
|
||||
}
|
||||
|
||||
public void SetHasProp(int prop, bool value = true)
|
||||
{
|
||||
var bitOffset = prop >> 3;
|
||||
SAV.SetFlag(Data, PropOffset + bitOffset, prop & 7, value);
|
||||
FlagUtil.SetFlag(Data, PropOffset + bitOffset, prop & 7, value);
|
||||
}
|
||||
}
|
||||
|
||||
21
PKHeX.Core/Saves/Substructures/Gen5/MusicalShow5.cs
Normal file
21
PKHeX.Core/Saves/Substructures/Gen5/MusicalShow5.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class MusicalShow5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE_BW = 0x1FC00; // footers + align = 0x20000
|
||||
public const int SIZE_B2W2 = 0x17C00; // footers + align = 0x18000
|
||||
public const string Extension = "pms";
|
||||
|
||||
public Span<byte> Data => Raw.Span;
|
||||
|
||||
public int Length
|
||||
{
|
||||
get => ReadInt32LittleEndian(Data);
|
||||
set => WriteInt32LittleEndian(Data, value);
|
||||
}
|
||||
|
||||
public Memory<byte> NARC => Raw.Slice(0x10, Length);
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Combined save block; 0x100 for first, 0x100 for second.
|
||||
/// Player data as well as ExtData flagging.
|
||||
/// </summary>
|
||||
public sealed class PlayerData5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
||||
public abstract class PlayerData5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
||||
{
|
||||
private Span<byte> OriginalTrainerTrash => Data.Slice(4, 0x10);
|
||||
|
||||
@@ -83,12 +83,143 @@ public int PlayedSeconds
|
||||
get => Data[0x24 + 3];
|
||||
set => Data[0x24 + 3] = (byte)value;
|
||||
}
|
||||
|
||||
// 0x25-0x2F ???
|
||||
|
||||
protected const ushort C21E = 0xC21E;
|
||||
|
||||
public bool IsMagicBattleVideo0 { get => ReadUInt16LittleEndian(Data[0x30..]) == C21E; set => WriteUInt16LittleEndian(Data[0x30..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicBattleVideo1 { get => ReadUInt16LittleEndian(Data[0x32..]) == C21E; set => WriteUInt16LittleEndian(Data[0x32..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicBattleVideo2 { get => ReadUInt16LittleEndian(Data[0x34..]) == C21E; set => WriteUInt16LittleEndian(Data[0x34..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicBattleVideo3 { get => ReadUInt16LittleEndian(Data[0x36..]) == C21E; set => WriteUInt16LittleEndian(Data[0x36..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicCGearSkin { get => ReadUInt16LittleEndian(Data[0x38..]) == C21E; set => WriteUInt16LittleEndian(Data[0x38..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicBattleTest { get => ReadUInt16LittleEndian(Data[0x3A..]) == C21E; set => WriteUInt16LittleEndian(Data[0x3A..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMusical { get => ReadUInt16LittleEndian(Data[0x3C..]) == C21E; set => WriteUInt16LittleEndian(Data[0x3C..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicPokeDexSkin { get => ReadUInt16LittleEndian(Data[0x3E..]) == C21E; set => WriteUInt16LittleEndian(Data[0x3E..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicHallOfFame { get => ReadUInt16LittleEndian(Data[0x40..]) == C21E; set => WriteUInt16LittleEndian(Data[0x40..], value ? C21E : (ushort)0); }
|
||||
// 0x42 unused in B/W
|
||||
|
||||
public abstract uint CountBattleVideo0 { get; set; }
|
||||
public abstract uint CountBattleVideo1 { get; set; }
|
||||
public abstract uint CountBattleVideo2 { get; set; }
|
||||
public abstract uint CountBattleVideo3 { get; set; }
|
||||
public abstract uint CountCGearSkin { get; set; }
|
||||
public abstract uint CountBattleTest { get; set; }
|
||||
public abstract uint CountMusical { get; set; }
|
||||
public abstract uint CountPokedexSkin { get; set; }
|
||||
public abstract uint CountHallOfFame { get; set; }
|
||||
|
||||
public abstract void UpdateExtData(ExtDataSectionNote5 section, uint count);
|
||||
public abstract (ushort, uint) GetExtData(ExtDataSectionNote5 section);
|
||||
}
|
||||
|
||||
public sealed class PlayerPosition5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
||||
public sealed class PlayerData5BW(SAV5 sav, Memory<byte> raw) : PlayerData5(sav, raw)
|
||||
{
|
||||
public int M { get => ReadInt32LittleEndian(Data[0x80..]); set => WriteUInt16LittleEndian(Data[0x80..], (ushort)value); }
|
||||
public int X { get => ReadUInt16LittleEndian(Data[0x86..]); set => WriteUInt16LittleEndian(Data[0x86..], (ushort)value); }
|
||||
public int Z { get => ReadUInt16LittleEndian(Data[0x8A..]); set => WriteUInt16LittleEndian(Data[0x8A..], (ushort)value); }
|
||||
public int Y { get => ReadUInt16LittleEndian(Data[0x8E..]); set => WriteUInt16LittleEndian(Data[0x8E..], (ushort)value); }
|
||||
public override uint CountBattleVideo0 { get => ReadUInt32LittleEndian(Data[0x44..]); set => WriteUInt32LittleEndian(Data[0x44..], value); }
|
||||
public override uint CountBattleVideo1 { get => ReadUInt32LittleEndian(Data[0x48..]); set => WriteUInt32LittleEndian(Data[0x48..], value); }
|
||||
public override uint CountBattleVideo2 { get => ReadUInt32LittleEndian(Data[0x4C..]); set => WriteUInt32LittleEndian(Data[0x4C..], value); }
|
||||
public override uint CountBattleVideo3 { get => ReadUInt32LittleEndian(Data[0x50..]); set => WriteUInt32LittleEndian(Data[0x50..], value); }
|
||||
public override uint CountCGearSkin { get => ReadUInt32LittleEndian(Data[0x54..]); set => WriteUInt32LittleEndian(Data[0x54..], value); }
|
||||
public override uint CountBattleTest { get => ReadUInt32LittleEndian(Data[0x58..]); set => WriteUInt32LittleEndian(Data[0x58..], value); }
|
||||
public override uint CountMusical { get => ReadUInt32LittleEndian(Data[0x5C..]); set => WriteUInt32LittleEndian(Data[0x5C..], value); }
|
||||
public override uint CountPokedexSkin { get => ReadUInt32LittleEndian(Data[0x60..]); set => WriteUInt32LittleEndian(Data[0x60..], value); }
|
||||
public override uint CountHallOfFame { get => ReadUInt32LittleEndian(Data[0x64..]); set => WriteUInt32LittleEndian(Data[0x64..], value); }
|
||||
|
||||
public override void UpdateExtData(ExtDataSectionNote5 section, uint count)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)section, (uint)ExtDataSectionNote5.HallOfFame, nameof(section));
|
||||
ushort value = count == 0 ? (ushort)0 : C21E;
|
||||
WriteUInt16LittleEndian(Data[(0x30 + ((int)section * 2))..], value);
|
||||
WriteUInt32LittleEndian(Data[(0x44 + ((int)section * 4))..], count);
|
||||
}
|
||||
|
||||
public override (ushort, uint) GetExtData(ExtDataSectionNote5 section)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)section, (uint)ExtDataSectionNote5.HallOfFame, nameof(section));
|
||||
var state = ReadUInt16LittleEndian(Data[(0x30 + ((int)section * 2))..]);
|
||||
var count = ReadUInt32LittleEndian(Data[(0x44 + ((int)section * 4))..]);
|
||||
return (state, count);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PlayerData5B2W2(SAV5B2W2 sav, Memory<byte> raw) : PlayerData5(sav, raw)
|
||||
{
|
||||
// Offsets are shifted due to the addition of new DLC blocks, which have the u16 and u32 counts.
|
||||
public bool IsMagicMovie1 { get => ReadUInt16LittleEndian(Data[0x42..]) == C21E; set => WriteUInt16LittleEndian(Data[0x42..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie2 { get => ReadUInt16LittleEndian(Data[0x44..]) == C21E; set => WriteUInt16LittleEndian(Data[0x44..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie3 { get => ReadUInt16LittleEndian(Data[0x46..]) == C21E; set => WriteUInt16LittleEndian(Data[0x46..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie4 { get => ReadUInt16LittleEndian(Data[0x48..]) == C21E; set => WriteUInt16LittleEndian(Data[0x48..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie5 { get => ReadUInt16LittleEndian(Data[0x4A..]) == C21E; set => WriteUInt16LittleEndian(Data[0x4A..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie6 { get => ReadUInt16LittleEndian(Data[0x4C..]) == C21E; set => WriteUInt16LittleEndian(Data[0x4C..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie7 { get => ReadUInt16LittleEndian(Data[0x4E..]) == C21E; set => WriteUInt16LittleEndian(Data[0x4E..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicMovie8 { get => ReadUInt16LittleEndian(Data[0x50..]) == C21E; set => WriteUInt16LittleEndian(Data[0x50..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicPWT1 { get => ReadUInt16LittleEndian(Data[0x52..]) == C21E; set => WriteUInt16LittleEndian(Data[0x52..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicPWT2 { get => ReadUInt16LittleEndian(Data[0x54..]) == C21E; set => WriteUInt16LittleEndian(Data[0x54..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicPWT3 { get => ReadUInt16LittleEndian(Data[0x56..]) == C21E; set => WriteUInt16LittleEndian(Data[0x56..], value ? C21E : (ushort)0); }
|
||||
public bool IsMagicUnused { get => ReadUInt16LittleEndian(Data[0x58..]) == C21E; set => WriteUInt16LittleEndian(Data[0x58..], value ? C21E : (ushort)0); }
|
||||
|
||||
public override uint CountBattleVideo0 { get => ReadUInt32LittleEndian(Data[0x5C..]); set => WriteUInt32LittleEndian(Data[0x5C..], value); }
|
||||
public override uint CountBattleVideo1 { get => ReadUInt32LittleEndian(Data[0x60..]); set => WriteUInt32LittleEndian(Data[0x60..], value); }
|
||||
public override uint CountBattleVideo2 { get => ReadUInt32LittleEndian(Data[0x64..]); set => WriteUInt32LittleEndian(Data[0x64..], value); }
|
||||
public override uint CountBattleVideo3 { get => ReadUInt32LittleEndian(Data[0x68..]); set => WriteUInt32LittleEndian(Data[0x68..], value); }
|
||||
public override uint CountCGearSkin { get => ReadUInt32LittleEndian(Data[0x6C..]); set => WriteUInt32LittleEndian(Data[0x6C..], value); }
|
||||
public override uint CountBattleTest { get => ReadUInt32LittleEndian(Data[0x70..]); set => WriteUInt32LittleEndian(Data[0x70..], value); }
|
||||
public override uint CountMusical { get => ReadUInt32LittleEndian(Data[0x74..]); set => WriteUInt32LittleEndian(Data[0x74..], value); }
|
||||
public override uint CountPokedexSkin { get => ReadUInt32LittleEndian(Data[0x78..]); set => WriteUInt32LittleEndian(Data[0x78..], value); }
|
||||
public override uint CountHallOfFame { get => ReadUInt32LittleEndian(Data[0x7C..]); set => WriteUInt32LittleEndian(Data[0x7C..], value); }
|
||||
public uint CountMovie1 { get => ReadUInt32LittleEndian(Data[0x80..]); set => WriteUInt32LittleEndian(Data[0x80..], value); }
|
||||
public uint CountMovie2 { get => ReadUInt32LittleEndian(Data[0x84..]); set => WriteUInt32LittleEndian(Data[0x84..], value); }
|
||||
public uint CountMovie3 { get => ReadUInt32LittleEndian(Data[0x88..]); set => WriteUInt32LittleEndian(Data[0x88..], value); }
|
||||
public uint CountMovie4 { get => ReadUInt32LittleEndian(Data[0x8C..]); set => WriteUInt32LittleEndian(Data[0x8C..], value); }
|
||||
public uint CountMovie5 { get => ReadUInt32LittleEndian(Data[0x90..]); set => WriteUInt32LittleEndian(Data[0x90..], value); }
|
||||
public uint CountMovie6 { get => ReadUInt32LittleEndian(Data[0x94..]); set => WriteUInt32LittleEndian(Data[0x94..], value); }
|
||||
public uint CountMovie7 { get => ReadUInt32LittleEndian(Data[0x98..]); set => WriteUInt32LittleEndian(Data[0x98..], value); }
|
||||
public uint CountMovie8 { get => ReadUInt32LittleEndian(Data[0x9C..]); set => WriteUInt32LittleEndian(Data[0x9C..], value); }
|
||||
public uint CountPWT1 { get => ReadUInt32LittleEndian(Data[0xA0..]); set => WriteUInt32LittleEndian(Data[0xA0..], value); }
|
||||
public uint CountPWT2 { get => ReadUInt32LittleEndian(Data[0xA4..]); set => WriteUInt32LittleEndian(Data[0xA4..], value); }
|
||||
public uint CountPWT3 { get => ReadUInt32LittleEndian(Data[0xA8..]); set => WriteUInt32LittleEndian(Data[0xA8..], value); }
|
||||
public uint CountUnused { get => ReadUInt32LittleEndian(Data[0xAC..]); set => WriteUInt32LittleEndian(Data[0xAC..], value); }
|
||||
|
||||
public override void UpdateExtData(ExtDataSectionNote5 section, uint count)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)section, (uint)ExtDataSectionNote5.Unused, nameof(section));
|
||||
ushort value = count == 0 ? (ushort)0 : C21E;
|
||||
WriteUInt16LittleEndian(Data[(0x30 + ((int)section * 2))..], value);
|
||||
WriteUInt32LittleEndian(Data[(0x5C + ((int)section * 4))..], count);
|
||||
}
|
||||
|
||||
public override (ushort, uint) GetExtData(ExtDataSectionNote5 section)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)section, (uint)ExtDataSectionNote5.HallOfFame, nameof(section));
|
||||
var state = ReadUInt16LittleEndian(Data[(0x30 + ((int)section * 2))..]);
|
||||
var count = ReadUInt32LittleEndian(Data[(0x5C + ((int)section * 4))..]);
|
||||
return (state, count);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ExtDataSectionNote5
|
||||
{
|
||||
BattleVideo0,
|
||||
BattleVideo1,
|
||||
BattleVideo2,
|
||||
BattleVideo3,
|
||||
CGearSkin,
|
||||
BattleTest,
|
||||
Musical,
|
||||
PokedexSkin,
|
||||
HallOfFame,
|
||||
|
||||
// B2/W2 additions
|
||||
Movie1,
|
||||
Movie2,
|
||||
Movie3,
|
||||
Movie4,
|
||||
Movie5,
|
||||
Movie6,
|
||||
Movie7,
|
||||
Movie8,
|
||||
PWT1,
|
||||
PWT2,
|
||||
PWT3,
|
||||
Unused,
|
||||
}
|
||||
|
||||
12
PKHeX.Core/Saves/Substructures/Gen5/PlayerPosition5.cs
Normal file
12
PKHeX.Core/Saves/Substructures/Gen5/PlayerPosition5.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class PlayerPosition5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
||||
{
|
||||
public int M { get => ReadInt32LittleEndian(Data[0x80..]); set => WriteUInt16LittleEndian(Data[0x80..], (ushort)value); }
|
||||
public int X { get => ReadUInt16LittleEndian(Data[0x86..]); set => WriteUInt16LittleEndian(Data[0x86..], (ushort)value); }
|
||||
public int Z { get => ReadUInt16LittleEndian(Data[0x8A..]); set => WriteUInt16LittleEndian(Data[0x8A..], (ushort)value); }
|
||||
public int Y { get => ReadUInt16LittleEndian(Data[0x8E..]); set => WriteUInt16LittleEndian(Data[0x8E..], (ushort)value); }
|
||||
}
|
||||
14
PKHeX.Core/Saves/Substructures/Gen5/PokestarMovie5.cs
Normal file
14
PKHeX.Core/Saves/Substructures/Gen5/PokestarMovie5.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class PokestarMovie5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE = 0x4B4;
|
||||
public const string Extension = "psm5";
|
||||
|
||||
private Span<byte> Data => Raw.Span;
|
||||
public bool IsUninitialized => !Data.ContainsAnyExcept<byte>(0xFF, 0);
|
||||
public string Name => IsUninitialized ? "Empty": "Movie";
|
||||
}
|
||||
48
PKHeX.Core/Saves/Substructures/Gen5/SkinInfo5.cs
Normal file
48
PKHeX.Core/Saves/Substructures/Gen5/SkinInfo5.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public abstract class SkinInfo5(SAV5 SAV, Memory<byte> raw) : SaveBlock<SAV5>(SAV, raw)
|
||||
{
|
||||
public abstract ushort CGearSkinChecksum { get; set; }
|
||||
public abstract bool HasCGearSkin { get; set; }
|
||||
}
|
||||
|
||||
public sealed class SkinInfo5BW(SAV5BW SAV, Memory<byte> raw) : SkinInfo5(SAV, raw)
|
||||
{
|
||||
public override ushort CGearSkinChecksum
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data[0x24..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x24..], value);
|
||||
}
|
||||
|
||||
public override bool HasCGearSkin
|
||||
{
|
||||
get => Data[0x26] == 1;
|
||||
set => Data[0x26] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SkinInfo5B2W2(SAV5B2W2 sav, Memory<byte> raw) : SkinInfo5(sav, raw)
|
||||
{
|
||||
public override ushort CGearSkinChecksum
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data[0x34..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x34..], value);
|
||||
}
|
||||
|
||||
public override bool HasCGearSkin
|
||||
{
|
||||
get => Data[0x36] == 1;
|
||||
set => Data[0x36] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
public Span<byte> SayingTrash => Data.Slice(0x80, 0x12);
|
||||
|
||||
public string Saying
|
||||
{
|
||||
get => sav.GetString(SayingTrash);
|
||||
set => sav.SetString(SayingTrash, value, 8, StringConverterOption.ClearZeroSafeTerminate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class PokeDexSkin5(Memory<byte> Raw)
|
||||
{
|
||||
public const string Extension = "pds";
|
||||
|
||||
/// <summary> Pixel width of the background image. </summary>
|
||||
public const int Width = 256; // px
|
||||
/// <summary> Pixel height of the background image. </summary>
|
||||
public const int Height = 192; // px
|
||||
|
||||
public const int SIZE = LengthTilePool + LengthColorData + LengthColorDataBackground + Unused + 4; // 0x6204
|
||||
|
||||
private const int TilePoolCount = 768; // 0x300
|
||||
private const int CountColorForeground = 16;
|
||||
private const int CountColorBackground = 64;
|
||||
|
||||
private const int LengthTilePool = TilePoolCount * PaletteTile.SIZE; // 0x6000
|
||||
private const int LengthColorData = CountColorForeground * sizeof(ushort); // 0x20
|
||||
private const int LengthColorDataBackground = CountColorBackground * sizeof(ushort); // 0x80
|
||||
private const int Unused = 0x160;
|
||||
|
||||
public Span<byte> Data => Raw.Span;
|
||||
public Span<byte> ColorChoices => Data[..LengthTilePool];
|
||||
public Span<byte> ColorForeground => Data.Slice(LengthTilePool, LengthColorData);
|
||||
public Span<byte> ColorBackground => Data.Slice(LengthTilePool + LengthColorData, LengthColorDataBackground);
|
||||
public bool IsUninitialized => !Data.ContainsAnyExcept<byte>(0xFF, 0);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public GeonetPoint GetCountrySubregion(byte country, byte subregion)
|
||||
{
|
||||
int index = GeonetOffset + ((country - 1) * 16) + (subregion / 4);
|
||||
int shift = 2 * (subregion % 4);
|
||||
return (GeonetPoint)(((Data[index] & 0b11 << shift) >> shift));
|
||||
return (GeonetPoint)((Data[index] & 0b11 << shift) >> shift);
|
||||
}
|
||||
|
||||
public void SetCountrySubregion(byte country, byte subregion, GeonetPoint point)
|
||||
|
||||
48
PKHeX.Core/Saves/Substructures/Gen5/WorldTournament5.cs
Normal file
48
PKHeX.Core/Saves/Substructures/Gen5/WorldTournament5.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class WorldTournament5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE = 0x1214;
|
||||
public const string Extension = "pwt";
|
||||
|
||||
private Span<byte> Data => Raw.Span;
|
||||
public bool IsUninitialized => !Data.ContainsAnyExcept<byte>(0xFF, 0);
|
||||
|
||||
private Span<byte> NameTrash => Data[0xA..0x54]; // 36 chars
|
||||
private Span<byte> DescriptionTrash => Data[0x54..0xEA]; // 74 chars
|
||||
private Span<byte> AboutTrash => Data[0xEA..0x1C8]; // 110 chars
|
||||
|
||||
public const int NameLength = 36; // chars
|
||||
public const int DescriptionLength = 74; // chars
|
||||
public const int AboutLength = 110; // chars
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => StringConverter5.GetString(NameTrash);
|
||||
set => StringConverter5.SetString(NameTrash, value, NameLength, 0, StringConverterOption.ClearFF);
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => StringConverter5.GetString(DescriptionTrash);
|
||||
set => StringConverter5.SetString(DescriptionTrash, value, DescriptionLength, 0, StringConverterOption.ClearFF);
|
||||
}
|
||||
|
||||
public string About
|
||||
{
|
||||
get => StringConverter5.GetString(AboutTrash);
|
||||
set => StringConverter5.SetString(AboutTrash, value, AboutLength, 0, StringConverterOption.ClearFF);
|
||||
}
|
||||
|
||||
public ushort Checksum
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data[^4..]);
|
||||
set => WriteUInt16LittleEndian(Data[^4..], value);
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum() => Checksums.CRC16_CCITT(Data[..^4]);
|
||||
public void RefreshChecksums() => Checksum = CalculateChecksum();
|
||||
}
|
||||
@@ -609,7 +609,7 @@ private static void OpenDialog(Form f)
|
||||
private void B_CellsStickers_Click(object sender, EventArgs e) => OpenDialog(new SAV_ZygardeCell((SAV7)SAV));
|
||||
private void B_LinkInfo_Click(object sender, EventArgs e) => OpenDialog(new SAV_Link6(SAV));
|
||||
private void B_OpenApricorn_Click(object sender, EventArgs e) => OpenDialog(new SAV_Apricorn((SAV4HGSS)SAV));
|
||||
private void B_CGearSkin_Click(object sender, EventArgs e) => OpenDialog(new SAV_CGearSkin((SAV5)SAV));
|
||||
private void B_CGearSkin_Click(object sender, EventArgs e) => OpenDialog(new SAV_DLC5((SAV5)SAV));
|
||||
private void B_OpenTrainerInfo_Click(object sender, EventArgs e) => OpenDialog(GetTrainerEditor(SAV));
|
||||
private void B_OpenOPowers_Click(object sender, EventArgs e) => OpenDialog(new SAV_OPower((ISaveBlock6Main)SAV));
|
||||
private void B_OpenHoneyTreeEditor_Click(object sender, EventArgs e) => OpenDialog(new SAV_HoneyTree((SAV4Sinnoh)SAV));
|
||||
|
||||
635
PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_DLC5.Designer.cs
generated
Normal file
635
PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_DLC5.Designer.cs
generated
Normal file
@@ -0,0 +1,635 @@
|
||||
using PKHeX.WinForms.Controls;
|
||||
|
||||
namespace PKHeX.WinForms
|
||||
{
|
||||
partial class SAV_DLC5
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
PB_CGearBackground = new System.Windows.Forms.PictureBox();
|
||||
B_Cancel = new System.Windows.Forms.Button();
|
||||
B_Save = new System.Windows.Forms.Button();
|
||||
B_ImportPNG = new System.Windows.Forms.Button();
|
||||
B_ExportPNG = new System.Windows.Forms.Button();
|
||||
B_ExportCGB = new System.Windows.Forms.Button();
|
||||
B_ImportCGB = new System.Windows.Forms.Button();
|
||||
TC_Tabs = new VerticalTabControl();
|
||||
Tab_CGear = new System.Windows.Forms.TabPage();
|
||||
Tab_PokeDex = new System.Windows.Forms.TabPage();
|
||||
B_PokeDexBackgroundLoad = new System.Windows.Forms.Button();
|
||||
B_PokeDexBackgroundSave = new System.Windows.Forms.Button();
|
||||
PB_PokeDexBackground = new System.Windows.Forms.PictureBox();
|
||||
PB_PokeDexForeground = new System.Windows.Forms.PictureBox();
|
||||
B_PokeDexSkinSave = new System.Windows.Forms.Button();
|
||||
B_PokeDexForegroundLoad = new System.Windows.Forms.Button();
|
||||
B_PokeDexSkinLoad = new System.Windows.Forms.Button();
|
||||
B_PokeDexForegroundSave = new System.Windows.Forms.Button();
|
||||
Tab_Musical = new System.Windows.Forms.TabPage();
|
||||
B_MusicalExport = new System.Windows.Forms.Button();
|
||||
B_MusicalImport = new System.Windows.Forms.Button();
|
||||
Tab_BattleVideo = new System.Windows.Forms.TabPage();
|
||||
B_BattleVideoExportDecrypted = new System.Windows.Forms.Button();
|
||||
B_BattleVideoExport = new System.Windows.Forms.Button();
|
||||
B_BattleVideoImport = new System.Windows.Forms.Button();
|
||||
LB_BattleVideo = new System.Windows.Forms.ListBox();
|
||||
Tab_Pokestar = new System.Windows.Forms.TabPage();
|
||||
B_PokestarExport = new System.Windows.Forms.Button();
|
||||
B_PokestarImport = new System.Windows.Forms.Button();
|
||||
LB_Pokestar = new System.Windows.Forms.ListBox();
|
||||
Tab_PWT = new System.Windows.Forms.TabPage();
|
||||
B_PWTExport = new System.Windows.Forms.Button();
|
||||
B_PWTImport = new System.Windows.Forms.Button();
|
||||
LB_PWT = new System.Windows.Forms.ListBox();
|
||||
Tab_MemoryLink = new System.Windows.Forms.TabPage();
|
||||
B_Memory2Export = new System.Windows.Forms.Button();
|
||||
B_Memory2Import = new System.Windows.Forms.Button();
|
||||
B_Memory1Export = new System.Windows.Forms.Button();
|
||||
B_Memory1Import = new System.Windows.Forms.Button();
|
||||
Tab_BattleTest = new System.Windows.Forms.TabPage();
|
||||
B_BattleTestExport = new System.Windows.Forms.Button();
|
||||
B_BattleTestImport = new System.Windows.Forms.Button();
|
||||
((System.ComponentModel.ISupportInitialize)PB_CGearBackground).BeginInit();
|
||||
TC_Tabs.SuspendLayout();
|
||||
Tab_CGear.SuspendLayout();
|
||||
Tab_PokeDex.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)PB_PokeDexBackground).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)PB_PokeDexForeground).BeginInit();
|
||||
Tab_Musical.SuspendLayout();
|
||||
Tab_BattleVideo.SuspendLayout();
|
||||
Tab_Pokestar.SuspendLayout();
|
||||
Tab_PWT.SuspendLayout();
|
||||
Tab_MemoryLink.SuspendLayout();
|
||||
Tab_BattleTest.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// PB_CGearBackground
|
||||
//
|
||||
PB_CGearBackground.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
PB_CGearBackground.Location = new System.Drawing.Point(7, 6);
|
||||
PB_CGearBackground.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
PB_CGearBackground.Name = "PB_CGearBackground";
|
||||
PB_CGearBackground.Size = new System.Drawing.Size(258, 194);
|
||||
PB_CGearBackground.TabIndex = 0;
|
||||
PB_CGearBackground.TabStop = false;
|
||||
//
|
||||
// B_Cancel
|
||||
//
|
||||
B_Cancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
||||
B_Cancel.Location = new System.Drawing.Point(407, 346);
|
||||
B_Cancel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_Cancel.Name = "B_Cancel";
|
||||
B_Cancel.Size = new System.Drawing.Size(120, 27);
|
||||
B_Cancel.TabIndex = 1;
|
||||
B_Cancel.Text = "Cancel";
|
||||
B_Cancel.UseVisualStyleBackColor = true;
|
||||
B_Cancel.Click += B_Cancel_Click;
|
||||
//
|
||||
// B_Save
|
||||
//
|
||||
B_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
||||
B_Save.Location = new System.Drawing.Point(535, 346);
|
||||
B_Save.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_Save.Name = "B_Save";
|
||||
B_Save.Size = new System.Drawing.Size(120, 27);
|
||||
B_Save.TabIndex = 2;
|
||||
B_Save.Text = "Save";
|
||||
B_Save.UseVisualStyleBackColor = true;
|
||||
B_Save.Click += B_Save_Click;
|
||||
//
|
||||
// B_ImportPNG
|
||||
//
|
||||
B_ImportPNG.Location = new System.Drawing.Point(273, 6);
|
||||
B_ImportPNG.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_ImportPNG.Name = "B_ImportPNG";
|
||||
B_ImportPNG.Size = new System.Drawing.Size(120, 32);
|
||||
B_ImportPNG.TabIndex = 3;
|
||||
B_ImportPNG.Text = "Load Image";
|
||||
B_ImportPNG.UseVisualStyleBackColor = true;
|
||||
B_ImportPNG.Click += B_ImportPNGCGear_Click;
|
||||
//
|
||||
// B_ExportPNG
|
||||
//
|
||||
B_ExportPNG.Location = new System.Drawing.Point(273, 44);
|
||||
B_ExportPNG.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_ExportPNG.Name = "B_ExportPNG";
|
||||
B_ExportPNG.Size = new System.Drawing.Size(120, 32);
|
||||
B_ExportPNG.TabIndex = 4;
|
||||
B_ExportPNG.Text = "Save Image";
|
||||
B_ExportPNG.UseVisualStyleBackColor = true;
|
||||
B_ExportPNG.Click += B_ExportPNGCGear_Click;
|
||||
//
|
||||
// B_ExportCGB
|
||||
//
|
||||
B_ExportCGB.Location = new System.Drawing.Point(273, 168);
|
||||
B_ExportCGB.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_ExportCGB.Name = "B_ExportCGB";
|
||||
B_ExportCGB.Size = new System.Drawing.Size(120, 32);
|
||||
B_ExportCGB.TabIndex = 6;
|
||||
B_ExportCGB.Text = "Export";
|
||||
B_ExportCGB.UseVisualStyleBackColor = true;
|
||||
B_ExportCGB.Click += B_ExportCGB_Click;
|
||||
//
|
||||
// B_ImportCGB
|
||||
//
|
||||
B_ImportCGB.Location = new System.Drawing.Point(273, 130);
|
||||
B_ImportCGB.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_ImportCGB.Name = "B_ImportCGB";
|
||||
B_ImportCGB.Size = new System.Drawing.Size(120, 32);
|
||||
B_ImportCGB.TabIndex = 5;
|
||||
B_ImportCGB.Text = "Import";
|
||||
B_ImportCGB.UseVisualStyleBackColor = true;
|
||||
B_ImportCGB.Click += B_ImportCGB_Click;
|
||||
//
|
||||
// TC_Tabs
|
||||
//
|
||||
TC_Tabs.Alignment = System.Windows.Forms.TabAlignment.Left;
|
||||
TC_Tabs.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
TC_Tabs.Controls.Add(Tab_CGear);
|
||||
TC_Tabs.Controls.Add(Tab_PokeDex);
|
||||
TC_Tabs.Controls.Add(Tab_BattleTest);
|
||||
TC_Tabs.Controls.Add(Tab_Musical);
|
||||
TC_Tabs.Controls.Add(Tab_BattleVideo);
|
||||
TC_Tabs.Controls.Add(Tab_Pokestar);
|
||||
TC_Tabs.Controls.Add(Tab_PWT);
|
||||
TC_Tabs.Controls.Add(Tab_MemoryLink);
|
||||
TC_Tabs.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
|
||||
TC_Tabs.ItemSize = new System.Drawing.Size(40, 128);
|
||||
TC_Tabs.Location = new System.Drawing.Point(0, 0);
|
||||
TC_Tabs.Margin = new System.Windows.Forms.Padding(0);
|
||||
TC_Tabs.Multiline = true;
|
||||
TC_Tabs.Name = "TC_Tabs";
|
||||
TC_Tabs.Padding = new System.Drawing.Point(0, 0);
|
||||
TC_Tabs.SelectedIndex = 0;
|
||||
TC_Tabs.Size = new System.Drawing.Size(663, 339);
|
||||
TC_Tabs.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
|
||||
TC_Tabs.TabIndex = 7;
|
||||
//
|
||||
// Tab_CGear
|
||||
//
|
||||
Tab_CGear.Controls.Add(PB_CGearBackground);
|
||||
Tab_CGear.Controls.Add(B_ExportCGB);
|
||||
Tab_CGear.Controls.Add(B_ImportPNG);
|
||||
Tab_CGear.Controls.Add(B_ImportCGB);
|
||||
Tab_CGear.Controls.Add(B_ExportPNG);
|
||||
Tab_CGear.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_CGear.Name = "Tab_CGear";
|
||||
Tab_CGear.Padding = new System.Windows.Forms.Padding(3);
|
||||
Tab_CGear.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_CGear.TabIndex = 0;
|
||||
Tab_CGear.Text = "C-Gear Skin";
|
||||
Tab_CGear.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Tab_PokeDex
|
||||
//
|
||||
Tab_PokeDex.Controls.Add(B_PokeDexBackgroundLoad);
|
||||
Tab_PokeDex.Controls.Add(B_PokeDexBackgroundSave);
|
||||
Tab_PokeDex.Controls.Add(PB_PokeDexBackground);
|
||||
Tab_PokeDex.Controls.Add(PB_PokeDexForeground);
|
||||
Tab_PokeDex.Controls.Add(B_PokeDexSkinSave);
|
||||
Tab_PokeDex.Controls.Add(B_PokeDexForegroundLoad);
|
||||
Tab_PokeDex.Controls.Add(B_PokeDexSkinLoad);
|
||||
Tab_PokeDex.Controls.Add(B_PokeDexForegroundSave);
|
||||
Tab_PokeDex.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_PokeDex.Name = "Tab_PokeDex";
|
||||
Tab_PokeDex.Padding = new System.Windows.Forms.Padding(3);
|
||||
Tab_PokeDex.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_PokeDex.TabIndex = 1;
|
||||
Tab_PokeDex.Text = "PokéDex Skin";
|
||||
Tab_PokeDex.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_PokeDexBackgroundLoad
|
||||
//
|
||||
B_PokeDexBackgroundLoad.Location = new System.Drawing.Point(403, 200);
|
||||
B_PokeDexBackgroundLoad.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_PokeDexBackgroundLoad.Name = "B_PokeDexBackgroundLoad";
|
||||
B_PokeDexBackgroundLoad.Size = new System.Drawing.Size(120, 32);
|
||||
B_PokeDexBackgroundLoad.TabIndex = 13;
|
||||
B_PokeDexBackgroundLoad.Text = "Load Image";
|
||||
B_PokeDexBackgroundLoad.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_PokeDexBackgroundSave
|
||||
//
|
||||
B_PokeDexBackgroundSave.Location = new System.Drawing.Point(403, 238);
|
||||
B_PokeDexBackgroundSave.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_PokeDexBackgroundSave.Name = "B_PokeDexBackgroundSave";
|
||||
B_PokeDexBackgroundSave.Size = new System.Drawing.Size(120, 32);
|
||||
B_PokeDexBackgroundSave.TabIndex = 14;
|
||||
B_PokeDexBackgroundSave.Text = "Save Image";
|
||||
B_PokeDexBackgroundSave.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// PB_PokeDexBackground
|
||||
//
|
||||
PB_PokeDexBackground.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
PB_PokeDexBackground.Location = new System.Drawing.Point(265, 0);
|
||||
PB_PokeDexBackground.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
PB_PokeDexBackground.Name = "PB_PokeDexBackground";
|
||||
PB_PokeDexBackground.Size = new System.Drawing.Size(258, 194);
|
||||
PB_PokeDexBackground.TabIndex = 12;
|
||||
PB_PokeDexBackground.TabStop = false;
|
||||
//
|
||||
// PB_PokeDexForeground
|
||||
//
|
||||
PB_PokeDexForeground.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
PB_PokeDexForeground.Location = new System.Drawing.Point(0, 0);
|
||||
PB_PokeDexForeground.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
PB_PokeDexForeground.Name = "PB_PokeDexForeground";
|
||||
PB_PokeDexForeground.Size = new System.Drawing.Size(258, 194);
|
||||
PB_PokeDexForeground.TabIndex = 7;
|
||||
PB_PokeDexForeground.TabStop = false;
|
||||
//
|
||||
// B_PokeDexSkinSave
|
||||
//
|
||||
B_PokeDexSkinSave.Location = new System.Drawing.Point(265, 293);
|
||||
B_PokeDexSkinSave.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_PokeDexSkinSave.Name = "B_PokeDexSkinSave";
|
||||
B_PokeDexSkinSave.Size = new System.Drawing.Size(120, 32);
|
||||
B_PokeDexSkinSave.TabIndex = 11;
|
||||
B_PokeDexSkinSave.Text = "Export";
|
||||
B_PokeDexSkinSave.UseVisualStyleBackColor = true;
|
||||
B_PokeDexSkinSave.Click += B_PokeDexSkinSave_Click;
|
||||
//
|
||||
// B_PokeDexForegroundLoad
|
||||
//
|
||||
B_PokeDexForegroundLoad.Location = new System.Drawing.Point(7, 200);
|
||||
B_PokeDexForegroundLoad.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_PokeDexForegroundLoad.Name = "B_PokeDexForegroundLoad";
|
||||
B_PokeDexForegroundLoad.Size = new System.Drawing.Size(120, 32);
|
||||
B_PokeDexForegroundLoad.TabIndex = 8;
|
||||
B_PokeDexForegroundLoad.Text = "Load Image";
|
||||
B_PokeDexForegroundLoad.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_PokeDexSkinLoad
|
||||
//
|
||||
B_PokeDexSkinLoad.Location = new System.Drawing.Point(138, 293);
|
||||
B_PokeDexSkinLoad.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_PokeDexSkinLoad.Name = "B_PokeDexSkinLoad";
|
||||
B_PokeDexSkinLoad.Size = new System.Drawing.Size(120, 32);
|
||||
B_PokeDexSkinLoad.TabIndex = 10;
|
||||
B_PokeDexSkinLoad.Text = "Import";
|
||||
B_PokeDexSkinLoad.UseVisualStyleBackColor = true;
|
||||
B_PokeDexSkinLoad.Click += B_PokeDexSkinLoad_Click;
|
||||
//
|
||||
// B_PokeDexForegroundSave
|
||||
//
|
||||
B_PokeDexForegroundSave.Location = new System.Drawing.Point(7, 238);
|
||||
B_PokeDexForegroundSave.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
B_PokeDexForegroundSave.Name = "B_PokeDexForegroundSave";
|
||||
B_PokeDexForegroundSave.Size = new System.Drawing.Size(120, 32);
|
||||
B_PokeDexForegroundSave.TabIndex = 9;
|
||||
B_PokeDexForegroundSave.Text = "Save Image";
|
||||
B_PokeDexForegroundSave.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Tab_Musical
|
||||
//
|
||||
Tab_Musical.Controls.Add(B_MusicalExport);
|
||||
Tab_Musical.Controls.Add(B_MusicalImport);
|
||||
Tab_Musical.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_Musical.Name = "Tab_Musical";
|
||||
Tab_Musical.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_Musical.TabIndex = 2;
|
||||
Tab_Musical.Text = "Musical";
|
||||
Tab_Musical.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_MusicalExport
|
||||
//
|
||||
B_MusicalExport.Location = new System.Drawing.Point(368, 3);
|
||||
B_MusicalExport.Name = "B_MusicalExport";
|
||||
B_MusicalExport.Size = new System.Drawing.Size(120, 48);
|
||||
B_MusicalExport.TabIndex = 11;
|
||||
B_MusicalExport.Text = "Export";
|
||||
B_MusicalExport.UseVisualStyleBackColor = true;
|
||||
B_MusicalExport.Click += B_MusicalExport_Click;
|
||||
//
|
||||
// B_MusicalImport
|
||||
//
|
||||
B_MusicalImport.Location = new System.Drawing.Point(243, 3);
|
||||
B_MusicalImport.Name = "B_MusicalImport";
|
||||
B_MusicalImport.Size = new System.Drawing.Size(120, 48);
|
||||
B_MusicalImport.TabIndex = 10;
|
||||
B_MusicalImport.Text = "Import";
|
||||
B_MusicalImport.UseVisualStyleBackColor = true;
|
||||
B_MusicalImport.Click += B_MusicalImport_Click;
|
||||
//
|
||||
// Tab_BattleVideo
|
||||
//
|
||||
Tab_BattleVideo.Controls.Add(B_BattleVideoExportDecrypted);
|
||||
Tab_BattleVideo.Controls.Add(B_BattleVideoExport);
|
||||
Tab_BattleVideo.Controls.Add(B_BattleVideoImport);
|
||||
Tab_BattleVideo.Controls.Add(LB_BattleVideo);
|
||||
Tab_BattleVideo.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_BattleVideo.Name = "Tab_BattleVideo";
|
||||
Tab_BattleVideo.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_BattleVideo.TabIndex = 3;
|
||||
Tab_BattleVideo.Text = "Battle Videos";
|
||||
Tab_BattleVideo.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_BattleVideoExportDecrypted
|
||||
//
|
||||
B_BattleVideoExportDecrypted.Location = new System.Drawing.Point(368, 111);
|
||||
B_BattleVideoExportDecrypted.Name = "B_BattleVideoExportDecrypted";
|
||||
B_BattleVideoExportDecrypted.Size = new System.Drawing.Size(120, 48);
|
||||
B_BattleVideoExportDecrypted.TabIndex = 10;
|
||||
B_BattleVideoExportDecrypted.Text = "Export Decrypted";
|
||||
B_BattleVideoExportDecrypted.UseVisualStyleBackColor = true;
|
||||
B_BattleVideoExportDecrypted.Click += B_BattleVideoExportDecrypted_Click;
|
||||
//
|
||||
// B_BattleVideoExport
|
||||
//
|
||||
B_BattleVideoExport.Location = new System.Drawing.Point(368, 3);
|
||||
B_BattleVideoExport.Name = "B_BattleVideoExport";
|
||||
B_BattleVideoExport.Size = new System.Drawing.Size(120, 48);
|
||||
B_BattleVideoExport.TabIndex = 8;
|
||||
B_BattleVideoExport.Text = "Export";
|
||||
B_BattleVideoExport.UseVisualStyleBackColor = true;
|
||||
B_BattleVideoExport.Click += B_BattleVideoExport_Click;
|
||||
//
|
||||
// B_BattleVideoImport
|
||||
//
|
||||
B_BattleVideoImport.Location = new System.Drawing.Point(243, 3);
|
||||
B_BattleVideoImport.Name = "B_BattleVideoImport";
|
||||
B_BattleVideoImport.Size = new System.Drawing.Size(120, 48);
|
||||
B_BattleVideoImport.TabIndex = 7;
|
||||
B_BattleVideoImport.Text = "Import";
|
||||
B_BattleVideoImport.UseVisualStyleBackColor = true;
|
||||
B_BattleVideoImport.Click += B_BattleVideoImport_Click;
|
||||
//
|
||||
// LB_BattleVideo
|
||||
//
|
||||
LB_BattleVideo.FormattingEnabled = true;
|
||||
LB_BattleVideo.ItemHeight = 15;
|
||||
LB_BattleVideo.Location = new System.Drawing.Point(3, 3);
|
||||
LB_BattleVideo.Name = "LB_BattleVideo";
|
||||
LB_BattleVideo.Size = new System.Drawing.Size(234, 64);
|
||||
LB_BattleVideo.TabIndex = 6;
|
||||
LB_BattleVideo.SelectedIndexChanged += LB_BattleVideo_SelectedIndexChanged;
|
||||
//
|
||||
// Tab_Pokestar
|
||||
//
|
||||
Tab_Pokestar.Controls.Add(B_PokestarExport);
|
||||
Tab_Pokestar.Controls.Add(B_PokestarImport);
|
||||
Tab_Pokestar.Controls.Add(LB_Pokestar);
|
||||
Tab_Pokestar.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_Pokestar.Name = "Tab_Pokestar";
|
||||
Tab_Pokestar.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_Pokestar.TabIndex = 5;
|
||||
Tab_Pokestar.Text = "Pokéstar Studios";
|
||||
Tab_Pokestar.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_PokestarExport
|
||||
//
|
||||
B_PokestarExport.Location = new System.Drawing.Point(368, 3);
|
||||
B_PokestarExport.Name = "B_PokestarExport";
|
||||
B_PokestarExport.Size = new System.Drawing.Size(120, 48);
|
||||
B_PokestarExport.TabIndex = 5;
|
||||
B_PokestarExport.Text = "Export";
|
||||
B_PokestarExport.UseVisualStyleBackColor = true;
|
||||
B_PokestarExport.Click += B_PokestarExport_Click;
|
||||
//
|
||||
// B_PokestarImport
|
||||
//
|
||||
B_PokestarImport.Location = new System.Drawing.Point(243, 3);
|
||||
B_PokestarImport.Name = "B_PokestarImport";
|
||||
B_PokestarImport.Size = new System.Drawing.Size(120, 48);
|
||||
B_PokestarImport.TabIndex = 4;
|
||||
B_PokestarImport.Text = "Import";
|
||||
B_PokestarImport.UseVisualStyleBackColor = true;
|
||||
B_PokestarImport.Click += B_PokestarImport_Click;
|
||||
//
|
||||
// LB_Pokestar
|
||||
//
|
||||
LB_Pokestar.FormattingEnabled = true;
|
||||
LB_Pokestar.ItemHeight = 15;
|
||||
LB_Pokestar.Location = new System.Drawing.Point(3, 3);
|
||||
LB_Pokestar.Name = "LB_Pokestar";
|
||||
LB_Pokestar.Size = new System.Drawing.Size(234, 124);
|
||||
LB_Pokestar.TabIndex = 3;
|
||||
LB_Pokestar.SelectedIndexChanged += LB_Pokestar_SelectedIndexChanged;
|
||||
//
|
||||
// Tab_PWT
|
||||
//
|
||||
Tab_PWT.Controls.Add(B_PWTExport);
|
||||
Tab_PWT.Controls.Add(B_PWTImport);
|
||||
Tab_PWT.Controls.Add(LB_PWT);
|
||||
Tab_PWT.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_PWT.Name = "Tab_PWT";
|
||||
Tab_PWT.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_PWT.TabIndex = 4;
|
||||
Tab_PWT.Text = "PWT";
|
||||
Tab_PWT.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_PWTExport
|
||||
//
|
||||
B_PWTExport.Location = new System.Drawing.Point(368, 3);
|
||||
B_PWTExport.Name = "B_PWTExport";
|
||||
B_PWTExport.Size = new System.Drawing.Size(120, 48);
|
||||
B_PWTExport.TabIndex = 2;
|
||||
B_PWTExport.Text = "Export";
|
||||
B_PWTExport.UseVisualStyleBackColor = true;
|
||||
B_PWTExport.Click += B_PWTExport_Click;
|
||||
//
|
||||
// B_PWTImport
|
||||
//
|
||||
B_PWTImport.Location = new System.Drawing.Point(243, 3);
|
||||
B_PWTImport.Name = "B_PWTImport";
|
||||
B_PWTImport.Size = new System.Drawing.Size(120, 48);
|
||||
B_PWTImport.TabIndex = 1;
|
||||
B_PWTImport.Text = "Import";
|
||||
B_PWTImport.UseVisualStyleBackColor = true;
|
||||
B_PWTImport.Click += B_PWTImport_Click;
|
||||
//
|
||||
// LB_PWT
|
||||
//
|
||||
LB_PWT.FormattingEnabled = true;
|
||||
LB_PWT.ItemHeight = 15;
|
||||
LB_PWT.Location = new System.Drawing.Point(3, 3);
|
||||
LB_PWT.Name = "LB_PWT";
|
||||
LB_PWT.Size = new System.Drawing.Size(234, 49);
|
||||
LB_PWT.TabIndex = 0;
|
||||
LB_PWT.SelectedIndexChanged += LB_PWTIndex_SelectedIndexChanged;
|
||||
//
|
||||
// Tab_MemoryLink
|
||||
//
|
||||
Tab_MemoryLink.Controls.Add(B_Memory2Export);
|
||||
Tab_MemoryLink.Controls.Add(B_Memory2Import);
|
||||
Tab_MemoryLink.Controls.Add(B_Memory1Export);
|
||||
Tab_MemoryLink.Controls.Add(B_Memory1Import);
|
||||
Tab_MemoryLink.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_MemoryLink.Name = "Tab_MemoryLink";
|
||||
Tab_MemoryLink.Padding = new System.Windows.Forms.Padding(3);
|
||||
Tab_MemoryLink.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_MemoryLink.TabIndex = 6;
|
||||
Tab_MemoryLink.Text = "Memory Link";
|
||||
Tab_MemoryLink.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_Memory2Export
|
||||
//
|
||||
B_Memory2Export.Location = new System.Drawing.Point(368, 57);
|
||||
B_Memory2Export.Name = "B_Memory2Export";
|
||||
B_Memory2Export.Size = new System.Drawing.Size(120, 48);
|
||||
B_Memory2Export.TabIndex = 6;
|
||||
B_Memory2Export.Text = "Export Slot 2";
|
||||
B_Memory2Export.UseVisualStyleBackColor = true;
|
||||
B_Memory2Export.Click += B_Memory2Export_Click;
|
||||
//
|
||||
// B_Memory2Import
|
||||
//
|
||||
B_Memory2Import.Location = new System.Drawing.Point(243, 57);
|
||||
B_Memory2Import.Name = "B_Memory2Import";
|
||||
B_Memory2Import.Size = new System.Drawing.Size(120, 48);
|
||||
B_Memory2Import.TabIndex = 5;
|
||||
B_Memory2Import.Text = "Import Slot 2";
|
||||
B_Memory2Import.UseVisualStyleBackColor = true;
|
||||
B_Memory2Import.Click += B_Memory2Import_Click;
|
||||
//
|
||||
// B_Memory1Export
|
||||
//
|
||||
B_Memory1Export.Location = new System.Drawing.Point(368, 3);
|
||||
B_Memory1Export.Name = "B_Memory1Export";
|
||||
B_Memory1Export.Size = new System.Drawing.Size(120, 48);
|
||||
B_Memory1Export.TabIndex = 4;
|
||||
B_Memory1Export.Text = "Export Slot 1";
|
||||
B_Memory1Export.UseVisualStyleBackColor = true;
|
||||
B_Memory1Export.Click += B_Memory1Export_Click;
|
||||
//
|
||||
// B_Memory1Import
|
||||
//
|
||||
B_Memory1Import.Location = new System.Drawing.Point(243, 3);
|
||||
B_Memory1Import.Name = "B_Memory1Import";
|
||||
B_Memory1Import.Size = new System.Drawing.Size(120, 48);
|
||||
B_Memory1Import.TabIndex = 3;
|
||||
B_Memory1Import.Text = "Import Slot 1";
|
||||
B_Memory1Import.UseVisualStyleBackColor = true;
|
||||
B_Memory1Import.Click += B_Memory1Import_Click;
|
||||
//
|
||||
// Tab_BattleTest
|
||||
//
|
||||
Tab_BattleTest.Controls.Add(B_BattleTestExport);
|
||||
Tab_BattleTest.Controls.Add(B_BattleTestImport);
|
||||
Tab_BattleTest.Location = new System.Drawing.Point(132, 4);
|
||||
Tab_BattleTest.Name = "Tab_BattleTest";
|
||||
Tab_BattleTest.Padding = new System.Windows.Forms.Padding(3);
|
||||
Tab_BattleTest.Size = new System.Drawing.Size(527, 331);
|
||||
Tab_BattleTest.TabIndex = 7;
|
||||
Tab_BattleTest.Text = "Battle Test";
|
||||
Tab_BattleTest.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// B_BattleTestExport
|
||||
//
|
||||
B_BattleTestExport.Location = new System.Drawing.Point(368, 3);
|
||||
B_BattleTestExport.Name = "B_BattleTestExport";
|
||||
B_BattleTestExport.Size = new System.Drawing.Size(120, 48);
|
||||
B_BattleTestExport.TabIndex = 13;
|
||||
B_BattleTestExport.Text = "Export";
|
||||
B_BattleTestExport.UseVisualStyleBackColor = true;
|
||||
B_BattleTestExport.Click += B_BattleTestExport_Click;
|
||||
//
|
||||
// B_BattleTestImport
|
||||
//
|
||||
B_BattleTestImport.Location = new System.Drawing.Point(243, 3);
|
||||
B_BattleTestImport.Name = "B_BattleTestImport";
|
||||
B_BattleTestImport.Size = new System.Drawing.Size(120, 48);
|
||||
B_BattleTestImport.TabIndex = 12;
|
||||
B_BattleTestImport.Text = "Import";
|
||||
B_BattleTestImport.UseVisualStyleBackColor = true;
|
||||
B_BattleTestImport.Click += B_BattleTestImport_Click;
|
||||
//
|
||||
// SAV_DLC5
|
||||
//
|
||||
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
|
||||
ClientSize = new System.Drawing.Size(664, 381);
|
||||
Controls.Add(TC_Tabs);
|
||||
Controls.Add(B_Save);
|
||||
Controls.Add(B_Cancel);
|
||||
Icon = Properties.Resources.Icon;
|
||||
Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
MaximizeBox = false;
|
||||
MaximumSize = new System.Drawing.Size(1080, 720);
|
||||
MinimumSize = new System.Drawing.Size(680, 420);
|
||||
Name = "SAV_DLC5";
|
||||
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
Text = "Generation 5 DLC I/O";
|
||||
((System.ComponentModel.ISupportInitialize)PB_CGearBackground).EndInit();
|
||||
TC_Tabs.ResumeLayout(false);
|
||||
Tab_CGear.ResumeLayout(false);
|
||||
Tab_PokeDex.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)PB_PokeDexBackground).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)PB_PokeDexForeground).EndInit();
|
||||
Tab_Musical.ResumeLayout(false);
|
||||
Tab_BattleVideo.ResumeLayout(false);
|
||||
Tab_Pokestar.ResumeLayout(false);
|
||||
Tab_PWT.ResumeLayout(false);
|
||||
Tab_MemoryLink.ResumeLayout(false);
|
||||
Tab_BattleTest.ResumeLayout(false);
|
||||
ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.PictureBox PB_CGearBackground;
|
||||
private System.Windows.Forms.Button B_Cancel;
|
||||
private System.Windows.Forms.Button B_Save;
|
||||
private System.Windows.Forms.Button B_ImportPNG;
|
||||
private System.Windows.Forms.Button B_ExportPNG;
|
||||
private System.Windows.Forms.Button B_ExportCGB;
|
||||
private System.Windows.Forms.Button B_ImportCGB;
|
||||
private VerticalTabControl TC_Tabs;
|
||||
private System.Windows.Forms.TabPage Tab_CGear;
|
||||
private System.Windows.Forms.TabPage Tab_PokeDex;
|
||||
private System.Windows.Forms.TabPage Tab_Musical;
|
||||
private System.Windows.Forms.TabPage Tab_BattleVideo;
|
||||
private System.Windows.Forms.TabPage Tab_PWT;
|
||||
private System.Windows.Forms.TabPage Tab_Pokestar;
|
||||
private System.Windows.Forms.TabPage Tab_MemoryLink;
|
||||
private System.Windows.Forms.Button B_PWTExport;
|
||||
private System.Windows.Forms.Button B_PWTImport;
|
||||
private System.Windows.Forms.ListBox LB_PWT;
|
||||
private System.Windows.Forms.Button B_PokestarExport;
|
||||
private System.Windows.Forms.Button B_PokestarImport;
|
||||
private System.Windows.Forms.ListBox LB_Pokestar;
|
||||
private System.Windows.Forms.Button B_BattleVideoExport;
|
||||
private System.Windows.Forms.Button B_BattleVideoImport;
|
||||
private System.Windows.Forms.ListBox LB_BattleVideo;
|
||||
private System.Windows.Forms.Button B_MusicalExport;
|
||||
private System.Windows.Forms.Button B_MusicalImport;
|
||||
private System.Windows.Forms.Button B_Memory2Export;
|
||||
private System.Windows.Forms.Button B_Memory2Import;
|
||||
private System.Windows.Forms.Button B_Memory1Export;
|
||||
private System.Windows.Forms.Button B_Memory1Import;
|
||||
private System.Windows.Forms.Button B_PokeDexBackgroundLoad;
|
||||
private System.Windows.Forms.Button B_PokeDexBackgroundSave;
|
||||
private System.Windows.Forms.PictureBox PB_PokeDexBackground;
|
||||
private System.Windows.Forms.PictureBox PB_PokeDexForeground;
|
||||
private System.Windows.Forms.Button B_PokeDexSkinSave;
|
||||
private System.Windows.Forms.Button B_PokeDexForegroundLoad;
|
||||
private System.Windows.Forms.Button B_PokeDexSkinLoad;
|
||||
private System.Windows.Forms.Button B_PokeDexForegroundSave;
|
||||
private System.Windows.Forms.Button B_BattleVideoExportDecrypted;
|
||||
private System.Windows.Forms.TabPage Tab_BattleTest;
|
||||
private System.Windows.Forms.Button B_BattleTestExport;
|
||||
private System.Windows.Forms.Button B_BattleTestImport;
|
||||
}
|
||||
}
|
||||
459
PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_DLC5.cs
Normal file
459
PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_DLC5.cs
Normal file
@@ -0,0 +1,459 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
|
||||
namespace PKHeX.WinForms;
|
||||
|
||||
public partial class SAV_DLC5 : Form
|
||||
{
|
||||
private readonly SAV5 Origin;
|
||||
private readonly SAV5 SAV;
|
||||
|
||||
private readonly CGearBackground bg;
|
||||
private readonly PokeDexSkin5 dex;
|
||||
private const string CGearFilterBW = $"PokeStock C-Gear Skin Background|*.{CGearBackgroundBW.Extension}";
|
||||
private const string CGearFilterB2W2 = $"C-Gear Background|*.{CGearBackgroundB2W2.Extension}";
|
||||
|
||||
private const string PWTFileName = "PWT";
|
||||
private const string MusicalShowFileName = "Musical Show";
|
||||
private const string BattleVideoFileName = "Generation 5 Battle Video";
|
||||
private const string PokeStarMovieFileName = "Pokestar Studio Movie";
|
||||
private const string MemoryLinkFileName = "Memory Link";
|
||||
private const string PokeDexFileName = "Pokédex Skin";
|
||||
private const string BattleTestFileName = "Battle Test";
|
||||
|
||||
private const string MemoryLinkExtension = "ml5";
|
||||
|
||||
public SAV_DLC5(SAV5 sav)
|
||||
{
|
||||
InitializeComponent();
|
||||
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
|
||||
SAV = (SAV5)(Origin = sav).Clone();
|
||||
|
||||
var data = SAV.CGearSkinData;
|
||||
bg = sav is SAV5BW ? new CGearBackgroundBW(data) : new CGearBackgroundB2W2(data);
|
||||
dex = new PokeDexSkin5(sav.PokedexSkinData);
|
||||
|
||||
LoadTabs();
|
||||
|
||||
Tab_BattleTest.Controls.Add(new Label
|
||||
{
|
||||
Text = "Not working. Needs research.",
|
||||
ForeColor = Color.Red, Location = new(20,20),
|
||||
AutoSize = true,
|
||||
});
|
||||
}
|
||||
|
||||
private void LoadTabs()
|
||||
{
|
||||
if (SAV is not SAV5B2W2 b2w2)
|
||||
{
|
||||
TC_Tabs.Controls.Remove(Tab_PWT);
|
||||
TC_Tabs.Controls.Remove(Tab_Pokestar);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadPWT(b2w2);
|
||||
LoadPokestar(b2w2);
|
||||
}
|
||||
|
||||
LoadCGear(SAV);
|
||||
LoadPokedexSkin(SAV);
|
||||
LoadBattleVideos(SAV);
|
||||
LoadMusical(SAV);
|
||||
LoadMemoryLink(SAV);
|
||||
LoadBattleTest(SAV);
|
||||
}
|
||||
|
||||
private void LoadCGear(SAV5 sav)
|
||||
{
|
||||
if (bg.IsUninitialized)
|
||||
{
|
||||
B_ExportCGB.Enabled = B_ExportPNG.Enabled = false;
|
||||
PB_CGearBackground.BackColor = Color.Black;
|
||||
return;
|
||||
}
|
||||
PB_CGearBackground.Image = CGearImage.GetBitmap(bg);
|
||||
}
|
||||
|
||||
private void LoadPokedexSkin(SAV5 sav)
|
||||
{
|
||||
if (dex.IsUninitialized)
|
||||
{
|
||||
B_PokeDexSkinSave.Enabled = false;
|
||||
PB_PokeDexBackground.BackColor = PB_PokeDexForeground.BackColor = Color.Black;
|
||||
return;
|
||||
}
|
||||
LoadPokedexSkin(dex.Data);
|
||||
}
|
||||
|
||||
private void LoadPokedexSkin(ReadOnlySpan<byte> data)
|
||||
{
|
||||
// not implemented
|
||||
}
|
||||
|
||||
private void LoadBattleTest(SAV5 sav)
|
||||
{
|
||||
// not implemented
|
||||
}
|
||||
|
||||
private void LoadMusical(SAV5 sav) { }
|
||||
private void LoadMemoryLink(SAV5 sav) { }
|
||||
|
||||
private void LoadBattleVideos(SAV5 sav)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var data = sav.GetBattleVideo(i);
|
||||
var bvid = new BattleVideo5(data);
|
||||
var name = bvid.IsUninitialized ? "Empty" : bvid.GetTrainerNames();
|
||||
LB_BattleVideo.Items.Add($"{i:00} - {name}");
|
||||
}
|
||||
LB_BattleVideo.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
private void LoadPokestar(SAV5B2W2 sav)
|
||||
{
|
||||
for (int i = 0; i < SAV5B2W2.PokestarCount; i++)
|
||||
{
|
||||
var data = sav.GetPokestarMovie(i);
|
||||
var movie = new PokestarMovie5(data);
|
||||
LB_Pokestar.Items.Add($"{i + 1:00} - {movie.Name}");
|
||||
}
|
||||
LB_Pokestar.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
private void LoadPWT(SAV5B2W2 sav)
|
||||
{
|
||||
for (int i = 0; i < SAV5B2W2.PWTCount; i++)
|
||||
{
|
||||
var data = sav.GetPWT(i);
|
||||
var pwt = new WorldTournament5(data);
|
||||
var name = pwt.Name;
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
name = "Empty";
|
||||
LB_PWT.Items.Add($"{i + 1:00} - {name}");
|
||||
}
|
||||
LB_PWT.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
private string? LastImportedFile = null;
|
||||
|
||||
private bool ImportFile(string extension, string name, int expectSize, out byte[] data, string? initialName = null, int otherSize = -1)
|
||||
{
|
||||
data = [];
|
||||
|
||||
using var ofd = new OpenFileDialog();
|
||||
ofd.Filter = $"{name}|*.{extension}";
|
||||
ofd.FileName = $"{initialName ?? name}.{extension}";
|
||||
if (ofd.ShowDialog() != DialogResult.OK)
|
||||
return false;
|
||||
var fi = new FileInfo(ofd.FileName);
|
||||
if (fi.Length != expectSize && fi.Length != otherSize)
|
||||
{
|
||||
WinFormsUtil.Error($"Invalid file size. Expected {expectSize} bytes, got {fi.Length} bytes.");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = File.ReadAllBytes(ofd.FileName);
|
||||
if (data.Length == otherSize)
|
||||
Array.Resize(ref data, expectSize);
|
||||
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
LastImportedFile = ofd.FileName;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ExportFile(string extension, string name, byte[] data, string? initialName = null)
|
||||
{
|
||||
using var sfd = new SaveFileDialog();
|
||||
sfd.Filter = $"{name}|*.{extension}";
|
||||
if (string.IsNullOrWhiteSpace(initialName))
|
||||
initialName = string.IsNullOrWhiteSpace(name) ? "Empty" : name;
|
||||
sfd.FileName = $"{initialName}.{extension}";
|
||||
if (sfd.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
File.WriteAllBytes(sfd.FileName, data);
|
||||
}
|
||||
|
||||
private void B_ImportPNGCGear_Click(object sender, EventArgs e)
|
||||
{
|
||||
using var ofd = new OpenFileDialog();
|
||||
ofd.Filter = "PNG File|*.png";
|
||||
ofd.FileName = "Background.png";
|
||||
if (ofd.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using var img = (Bitmap)Image.FromFile(ofd.FileName);
|
||||
if (!IsInputCorrect<CGearBackground>(img, out var msg))
|
||||
{
|
||||
WinFormsUtil.Alert(msg);
|
||||
return;
|
||||
}
|
||||
var result = CGearImage.GetCGearBackground(img, bg);
|
||||
SAV.SetCGearSkin(bg.Data);
|
||||
PB_CGearBackground.Image = CGearImage.GetBitmap(bg); // regenerate rather than reuse input
|
||||
B_ExportCGB.Enabled = B_ExportPNG.Enabled = true;
|
||||
if (CheckResult<CGearBackground>(result, out msg))
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
else
|
||||
WinFormsUtil.Alert(msg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WinFormsUtil.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsInputCorrect<T>(Bitmap img, [NotNullWhen(false)] out string? msg) where T : ITiledImage
|
||||
{
|
||||
if (img.Width != T.Width || img.Height != T.Height)
|
||||
{
|
||||
msg = $"Incorrect image dimensions. Expected {T.Width}x{T.Height}";
|
||||
return false;
|
||||
}
|
||||
if (img.PixelFormat != PixelFormat.Format32bppArgb)
|
||||
{
|
||||
msg = $"Incorrect image format. Expected {PixelFormat.Format32bppArgb}";
|
||||
return false;
|
||||
}
|
||||
msg = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CheckResult<T>(TiledImageStat result, [NotNullWhen(false)] out string? msg)
|
||||
where T : ITiledImage
|
||||
{
|
||||
bool tooManyColors = result.ColorCount > T.ColorCount;
|
||||
bool tooManyTiles = result.TileCount > T.TilePoolCount;
|
||||
if (!tooManyColors && !tooManyTiles)
|
||||
{
|
||||
msg = null;
|
||||
return true; // Success
|
||||
}
|
||||
|
||||
msg = "";
|
||||
if (tooManyColors)
|
||||
msg += $"Too many colors. Expected: {T.ColorCount}, received {result.ColorCount}";
|
||||
if (tooManyTiles)
|
||||
msg += (msg.Length != 0 ? Environment.NewLine : "") + $"Too many tiles. Expected {T.TilePoolCount}, received {result.TileCount}";
|
||||
return false;
|
||||
}
|
||||
|
||||
private void B_ExportPNGCGear_Click(object sender, EventArgs e)
|
||||
{
|
||||
using var sfd = new SaveFileDialog();
|
||||
sfd.Filter = "PNG File|*.png";
|
||||
sfd.FileName = "Background.png";
|
||||
if (sfd.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
PB_CGearBackground.Image.Save(sfd.FileName, ImageFormat.Png);
|
||||
}
|
||||
|
||||
private void B_ImportCGB_Click(object sender, EventArgs e)
|
||||
{
|
||||
using var ofd = new OpenFileDialog();
|
||||
bool isBW = SAV is SAV5BW;
|
||||
ofd.Filter = isBW ? $"{CGearFilterBW}|{CGearFilterB2W2}" : $"{CGearFilterB2W2}|{CGearFilterBW}";
|
||||
|
||||
if (ofd.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var path = ofd.FileName;
|
||||
var len = new FileInfo(path).Length;
|
||||
if (len != CGearBackground.SIZE)
|
||||
{
|
||||
WinFormsUtil.Error($"Incorrect size, got {len} bytes, expected {CGearBackground.SIZE} bytes.");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
|
||||
// Load the data and adjust it to the correct game format if not matching.
|
||||
CGearBackground temp = isBW ? new CGearBackgroundBW(data) : new CGearBackgroundB2W2(data);
|
||||
bool isPSK = PaletteTileSelection.IsPaletteShiftFormat(temp.Arrange);
|
||||
|
||||
try
|
||||
{
|
||||
if (isBW && !isPSK)
|
||||
PaletteTileSelection.ConvertToShiftFormat<CGearBackgroundBW>(temp.Arrange);
|
||||
else if (!isBW && isPSK)
|
||||
PaletteTileSelection.ConvertFromShiftFormat(temp.Arrange);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WinFormsUtil.Error(ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
SAV.SetCGearSkin(temp.Data);
|
||||
PB_CGearBackground.Image = CGearImage.GetBitmap(bg);
|
||||
B_ExportCGB.Enabled = B_ExportPNG.Enabled = true;
|
||||
}
|
||||
|
||||
private void B_ExportCGB_Click(object sender, EventArgs e)
|
||||
{
|
||||
using var sfd = new SaveFileDialog();
|
||||
sfd.Filter = SAV is SAV5BW ? CGearFilterBW : CGearFilterB2W2;
|
||||
|
||||
if (sfd.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
File.WriteAllBytes(sfd.FileName, bg.Data.ToArray());
|
||||
}
|
||||
|
||||
private void B_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
Origin.CopyChangesFrom(SAV);
|
||||
Close();
|
||||
}
|
||||
|
||||
private void B_Cancel_Click(object sender, EventArgs e) => Close();
|
||||
|
||||
private void B_PWTImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
const int pporg = 0x1314;
|
||||
if (!ImportFile(WorldTournament5.Extension, PWTFileName, WorldTournament5.SIZE, out var data, otherSize: pporg))
|
||||
return;
|
||||
var index = LB_PWT.SelectedIndex;
|
||||
var b2w2 = (SAV5B2W2)SAV;
|
||||
b2w2.SetPWT(index, data);
|
||||
var pwt = new WorldTournament5(data);
|
||||
var name = pwt.Name;
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
name = "Empty";
|
||||
LB_PWT.Items[index] = $"{index + 1:00} - {name}";
|
||||
LB_PWTIndex_SelectedIndexChanged(sender, e);
|
||||
}
|
||||
|
||||
private void B_PWTExport_Click(object sender, EventArgs e)
|
||||
{
|
||||
var b2w2 = (SAV5B2W2)SAV;
|
||||
var data = b2w2.GetPWT(LB_PWT.SelectedIndex);
|
||||
var pwt = new WorldTournament5(data);
|
||||
var name = pwt.Name;
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
name = "Empty";
|
||||
ExportFile(WorldTournament5.Extension, PWTFileName, data.ToArray(), name);
|
||||
}
|
||||
|
||||
private void B_MusicalImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
var size = SAV is SAV5B2W2 ? MusicalShow5.SIZE_B2W2 : MusicalShow5.SIZE_BW;
|
||||
const int pporg = 0x17D78;
|
||||
if (!ImportFile(MusicalShow5.Extension, MusicalShowFileName, size, out var data, otherSize: pporg))
|
||||
return;
|
||||
SAV.SetMusical(data);
|
||||
if (LastImportedFile is { } name)
|
||||
SAV.Musical.MusicalName = Path.GetFileNameWithoutExtension(name).Trim();
|
||||
}
|
||||
|
||||
private void B_MusicalExport_Click(object sender, EventArgs e)
|
||||
{
|
||||
var data = SAV.MusicalDownloadData;
|
||||
ExportFile(MusicalShow5.Extension, SAV.Musical.MusicalName, data.ToArray());
|
||||
}
|
||||
|
||||
private void B_BattleVideoImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ImportFile(BattleVideo5.Extension, BattleVideoFileName, BattleVideo5.SIZE, out var data))
|
||||
return;
|
||||
bool decrypted = BattleVideo5.GetIsDecrypted(data);
|
||||
var bvid = new BattleVideo5(data) { IsDecrypted = decrypted };
|
||||
bvid.Encrypt();
|
||||
bvid.RefreshChecksums();
|
||||
|
||||
var index = LB_BattleVideo.SelectedIndex;
|
||||
SAV.SetBattleVideo(index, data);
|
||||
var name = bvid.IsUninitialized ? "Empty" : bvid.GetTrainerNames();
|
||||
LB_BattleVideo.Items[index] = $"{index:00} - {name}";
|
||||
LB_BattleVideo_SelectedIndexChanged(sender, e);
|
||||
}
|
||||
|
||||
private void B_BattleVideoExport_Click(object sender, EventArgs e)
|
||||
{
|
||||
var index = LB_BattleVideo.SelectedIndex;
|
||||
var data = SAV.GetBattleVideo(index);
|
||||
ExportFile(BattleVideo5.Extension, BattleVideoFileName, data.ToArray());
|
||||
}
|
||||
|
||||
private void B_BattleVideoExportDecrypted_Click(object sender, EventArgs e)
|
||||
{
|
||||
var index = LB_BattleVideo.SelectedIndex;
|
||||
var data = SAV.GetBattleVideo(index);
|
||||
var bvid = new BattleVideo5(data);
|
||||
bool actual = !bvid.IsUninitialized;
|
||||
if (actual)
|
||||
bvid.Decrypt();
|
||||
ExportFile(BattleVideo5.Extension, BattleVideoFileName, data.ToArray());
|
||||
if (actual)
|
||||
bvid.Encrypt();
|
||||
}
|
||||
|
||||
private void B_PokestarImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ImportFile(PokestarMovie5.Extension, PokeStarMovieFileName, PokestarMovie5.SIZE, out var data))
|
||||
return;
|
||||
var index = LB_Pokestar.SelectedIndex;
|
||||
var b2w2 = (SAV5B2W2)SAV;
|
||||
b2w2.SetPokestarMovie(index, data);
|
||||
var movie = new PokestarMovie5(data);
|
||||
LB_Pokestar.Items[index] = $"{+1:00} - {movie.Name}";
|
||||
LB_Pokestar_SelectedIndexChanged(sender, e);
|
||||
}
|
||||
|
||||
private void B_PokestarExport_Click(object sender, EventArgs e)
|
||||
{
|
||||
var b2w2 = (SAV5B2W2)SAV;
|
||||
var data = b2w2.GetPokestarMovie(LB_Pokestar.SelectedIndex);
|
||||
ExportFile(PokestarMovie5.Extension, PokeStarMovieFileName, data.ToArray());
|
||||
}
|
||||
|
||||
private void LB_Pokestar_SelectedIndexChanged(object sender, EventArgs e) { }
|
||||
private void LB_PWTIndex_SelectedIndexChanged(object sender, EventArgs e) { }
|
||||
private void LB_BattleVideo_SelectedIndexChanged(object sender, EventArgs e) { }
|
||||
|
||||
private void B_Memory1Import_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ImportFile(MemoryLinkExtension, MemoryLinkFileName, SAV.Link1Data.Length, out var data))
|
||||
return;
|
||||
SAV.SetLink1Data(data);
|
||||
}
|
||||
|
||||
private void B_Memory2Import_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ImportFile(MemoryLinkExtension, MemoryLinkFileName, SAV.Link2Data.Length, out var data))
|
||||
return;
|
||||
SAV.SetLink2Data(data);
|
||||
}
|
||||
|
||||
private void B_Memory1Export_Click(object sender, EventArgs e)
|
||||
=> ExportFile(MemoryLinkExtension, MemoryLinkFileName, SAV.Link1Data.ToArray());
|
||||
private void B_Memory2Export_Click(object sender, EventArgs e)
|
||||
=> ExportFile(MemoryLinkExtension, MemoryLinkFileName, SAV.Link2Data.ToArray());
|
||||
private void B_PokeDexSkinSave_Click(object sender, EventArgs e)
|
||||
=> ExportFile(PokeDexSkin5.Extension, PokeDexFileName, SAV.PokedexSkinData.ToArray());
|
||||
private void B_BattleTestExport_Click(object sender, EventArgs e)
|
||||
=> ExportFile(BattleTest5.Extension, BattleTestFileName, SAV.BattleTest.ToArray());
|
||||
|
||||
private void B_PokeDexSkinLoad_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ImportFile(PokeDexSkin5.Extension, PokeDexFileName, SAV.PokedexSkinData.Length, out var data))
|
||||
return;
|
||||
SAV.SetPokeDexSkin(data);
|
||||
LoadPokedexSkin(data);
|
||||
}
|
||||
|
||||
private void B_BattleTestImport_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!ImportFile(BattleTest5.Extension, BattleTestFileName, BattleTest5.SIZE, out var data))
|
||||
return;
|
||||
SAV.SetBattleTest(data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user