mirror of
https://github.com/kwsch/PKHeX
synced 2024-12-01 08:19:15 +00:00
a1f2f8708a
pass identifier on cloning pkm remove unnecessary ref for passing Data (not modifying array reference) const some magic numbers
275 lines
10 KiB
C#
275 lines
10 KiB
C#
using System;
|
|
using System.Linq;
|
|
|
|
namespace PKHeX.Core
|
|
{
|
|
public enum InventoryType
|
|
{
|
|
Items,
|
|
KeyItems,
|
|
TMHMs,
|
|
Medicine,
|
|
Berries,
|
|
Balls,
|
|
BattleItems,
|
|
MailItems,
|
|
PCItems,
|
|
FreeSpace,
|
|
ZCrystals,
|
|
}
|
|
public class InventoryItem
|
|
{
|
|
public bool New;
|
|
public bool FreeSpace;
|
|
public int Index, Count;
|
|
public InventoryItem Clone() => (InventoryItem) MemberwiseClone();
|
|
|
|
// Check Pouch Compatibility
|
|
public bool Valid(ushort[] LegalItems, bool HaX, int MaxItemID)
|
|
{
|
|
if (Index == 0)
|
|
return true;
|
|
if (Index <= MaxItemID)
|
|
return HaX || LegalItems.Contains((ushort)Index);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class InventoryPouch
|
|
{
|
|
public readonly InventoryType Type;
|
|
public readonly ushort[] LegalItems;
|
|
public readonly int MaxCount;
|
|
public int Count => Items.Count(it => it.Count > 0);
|
|
public uint SecurityKey { private get; set; } // = 0 // Gen3 Only
|
|
public InventoryItem[] Items;
|
|
|
|
private readonly int Offset;
|
|
private readonly int PouchDataSize;
|
|
private InventoryItem[] OriginalItems;
|
|
|
|
public InventoryPouch(InventoryType type, ushort[] legal, int maxcount, int offset, int size = -1)
|
|
{
|
|
Type = type;
|
|
LegalItems = legal;
|
|
MaxCount = maxcount;
|
|
Offset = offset;
|
|
PouchDataSize = size > -1 ? size : legal.Length;
|
|
}
|
|
|
|
public void GetPouch(byte[] Data)
|
|
{
|
|
InventoryItem[] items = new InventoryItem[PouchDataSize];
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
items[i] = new InventoryItem
|
|
{
|
|
Index = BitConverter.ToUInt16(Data, Offset + i*4),
|
|
Count = BitConverter.ToUInt16(Data, Offset + i*4 + 2) ^ (ushort)SecurityKey
|
|
};
|
|
}
|
|
Items = items;
|
|
}
|
|
public void SetPouch(byte[] Data)
|
|
{
|
|
if (Items.Length != PouchDataSize)
|
|
throw new ArgumentException("Item array length does not match original pouch size.");
|
|
|
|
for (int i = 0; i < Items.Length; i++)
|
|
{
|
|
BitConverter.GetBytes((ushort)Items[i].Index).CopyTo(Data, Offset + i*4);
|
|
BitConverter.GetBytes((ushort)((ushort)Items[i].Count ^ (ushort)SecurityKey)).CopyTo(Data, Offset + i*4 + 2);
|
|
}
|
|
}
|
|
public void GetPouch7(byte[] Data)
|
|
{
|
|
InventoryItem[] items = new InventoryItem[PouchDataSize];
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
// 10bit itemID
|
|
// 10bit count
|
|
// 12bit flags/reserved
|
|
uint val = BitConverter.ToUInt32(Data, Offset + i*4);
|
|
items[i] = new InventoryItem
|
|
{
|
|
Index = (int)(val & 0x3FF),
|
|
Count = (int)(val >> 10 & 0x3FF),
|
|
New = (val & 0x40000000) != 0, // 30th bit is "NEW"
|
|
FreeSpace = (val >> 20 & 0x3FF) != 0, // "FREE SPACE" sortIndex
|
|
};
|
|
}
|
|
Items = items;
|
|
OriginalItems = Items.Select(i => i.Clone()).ToArray();
|
|
}
|
|
public void SetPouch7(byte[] Data, bool setNEW = false)
|
|
{
|
|
if (Items.Length != PouchDataSize)
|
|
throw new ArgumentException("Item array length does not match original pouch size.");
|
|
|
|
for (int i = 0; i < Items.Length; i++)
|
|
{
|
|
// Build Item Value
|
|
uint val = 0;
|
|
val |= (uint)(Items[i].Index & 0x3FF);
|
|
val |= (uint)(Items[i].Count & 0x3FF) << 10;
|
|
if (setNEW)
|
|
Items[i].New |= OriginalItems.All(z => z.Index != Items[i].Index);
|
|
if (Items[i].New)
|
|
val |= 0x40000000;
|
|
if (Items[i].FreeSpace)
|
|
val |= 0x100000;
|
|
BitConverter.GetBytes(val).CopyTo(Data, Offset + i * 4);
|
|
}
|
|
}
|
|
|
|
public void GetPouchBigEndian(ref byte[] Data)
|
|
{
|
|
InventoryItem[] items = new InventoryItem[PouchDataSize];
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
items[i] = new InventoryItem
|
|
{
|
|
Index = BigEndian.ToUInt16(Data, Offset + i * 4),
|
|
Count = BigEndian.ToUInt16(Data, Offset + i * 4 + 2) ^ (ushort)SecurityKey
|
|
};
|
|
}
|
|
Items = items;
|
|
}
|
|
public void SetPouchBigEndian(ref byte[] Data)
|
|
{
|
|
if (Items.Length != PouchDataSize)
|
|
throw new ArgumentException("Item array length does not match original pouch size.");
|
|
|
|
for (int i = 0; i < Items.Length; i++)
|
|
{
|
|
BigEndian.GetBytes((ushort)Items[i].Index).CopyTo(Data, Offset + i * 4);
|
|
BigEndian.GetBytes((ushort)((ushort)Items[i].Count ^ (ushort)SecurityKey)).CopyTo(Data, Offset + i * 4 + 2);
|
|
}
|
|
}
|
|
|
|
public void GetPouchG1(byte[] Data)
|
|
{
|
|
InventoryItem[] items = new InventoryItem[PouchDataSize];
|
|
if (Type == InventoryType.TMHMs)
|
|
{
|
|
int slot = 0;
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
if (Data[Offset + i] != 0)
|
|
items[slot++] = new InventoryItem
|
|
{
|
|
Index = LegalItems[i],
|
|
Count = Data[Offset+i]
|
|
};
|
|
}
|
|
while (slot < items.Length)
|
|
items[slot++] = new InventoryItem
|
|
{
|
|
Index = 0,
|
|
Count = 0
|
|
};
|
|
}
|
|
else
|
|
{
|
|
int numStored = Data[Offset];
|
|
if (numStored > PouchDataSize) // uninitialized yellow (0xFF), sanity check for out-of-bounds values
|
|
numStored = 0;
|
|
for (int i = 0; i < numStored; i++)
|
|
{
|
|
switch (Type)
|
|
{
|
|
case InventoryType.KeyItems:
|
|
items[i] = new InventoryItem
|
|
{
|
|
Index = Data[Offset + i + 1],
|
|
Count = 1
|
|
};
|
|
break;
|
|
default:
|
|
items[i] = new InventoryItem
|
|
{
|
|
Index = Data[Offset + i * 2 + 1],
|
|
Count = Data[Offset + i * 2 + 2]
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
for (int i = numStored; i < items.Length; i++)
|
|
{
|
|
items[i] = new InventoryItem
|
|
{
|
|
Index = 0,
|
|
Count = 0
|
|
};
|
|
}
|
|
}
|
|
Items = items;
|
|
|
|
}
|
|
public void SetPouchG1(byte[] Data)
|
|
{
|
|
if (Items.Length != PouchDataSize)
|
|
throw new ArgumentException("Item array length does not match original pouch size.");
|
|
switch (Type)
|
|
{
|
|
case InventoryType.TMHMs:
|
|
foreach (InventoryItem t in Items)
|
|
{
|
|
if (!LegalItems.Any(it => it == t.Index))
|
|
continue;
|
|
int index = Offset + Array.FindIndex(LegalItems, it => t.Index == it);
|
|
Data[index] = (byte)t.Count;
|
|
}
|
|
break;
|
|
case InventoryType.KeyItems:
|
|
for (int i = 0; i < Items.Length; i++)
|
|
{
|
|
Data[Offset + i + 1] = (byte)Items[i].Index;
|
|
}
|
|
Data[Offset] = (byte)Count;
|
|
Data[Offset + 1 + Count] = 0xFF;
|
|
break;
|
|
default:
|
|
for (int i = 0; i < Items.Length; i++)
|
|
{
|
|
Data[Offset + i * 2 + 1] = (byte)Items[i].Index;
|
|
Data[Offset + i * 2 + 2] = (byte)Items[i].Count;
|
|
}
|
|
Data[Offset] = (byte)Count;
|
|
Data[Offset + 1 + 2 * Count] = 0xFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void SortByCount(bool reverse = false)
|
|
{
|
|
var list = Items.Where(item => item.Index != 0).OrderBy(item => item.Count == 0);
|
|
list = reverse
|
|
? list.ThenByDescending(item => item.Count)
|
|
: list.ThenBy(item => item.Count);
|
|
Items = list.Concat(Items.Where(item => item.Index == 0)).ToArray();
|
|
}
|
|
public void SortByIndex(bool reverse = false)
|
|
{
|
|
var list = Items.Where(item => item.Index != 0).OrderBy(item => item.Count == 0);
|
|
list = reverse
|
|
? list.ThenByDescending(item => item.Index)
|
|
: list.ThenBy(item => item.Index);
|
|
Items = list.Concat(Items.Where(item => item.Index == 0)).ToArray();
|
|
}
|
|
public void SortByName(string[] names, bool reverse = false)
|
|
{
|
|
var list = Items.Where(item => item.Index != 0 && item.Index < names.Length).OrderBy(item => item.Count == 0);
|
|
list = reverse
|
|
? list.ThenByDescending(item => names[item.Index])
|
|
: list.ThenBy(item => names[item.Index]);
|
|
Items = list.Concat(Items.Where(item => item.Index == 0 || item.Index >= names.Length)).ToArray();
|
|
}
|
|
|
|
public void Sanitize(bool HaX, int MaxItemID)
|
|
{
|
|
var x = Items.Where(item => item.Valid(LegalItems, HaX, MaxItemID)).ToArray();
|
|
Items = x.Concat(new byte[PouchDataSize - x.Length].Select(i => new InventoryItem())).ToArray();
|
|
}
|
|
}
|
|
}
|