diff --git a/Misc/SAV6.cs b/Misc/SAV6.cs index b1708800c..4f5b2aaf0 100644 --- a/Misc/SAV6.cs +++ b/Misc/SAV6.cs @@ -518,9 +518,7 @@ public bool DaycareOccupied2 public byte[] Puffs { get { return Data.Skip(Puff).Take(100).ToArray(); } set { value.CopyTo(Data, Puff); } } public int PuffCount { get { return BitConverter.ToInt32(Data, Puff + 100); } set { BitConverter.GetBytes(value).CopyTo(Data, Puff + 100); } } - - public byte[] getWondercard(int i) { return Data.Skip(WondercardData + i*WC6.Size).Take(WC6.Size).ToArray(); } - public void setWondercard(int i, byte[] data) { data.CopyTo(data, WondercardData + i * WC6.Size); } + public string JPEGTitle => JPEG > -1 ? null : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A)); public byte[] JPEGData => JPEG > -1 || Data[JPEG + 0x54] != 0xFF ? null : Data.Skip(JPEG + 0x54).Take(0xE004).ToArray(); @@ -827,6 +825,60 @@ public PK6[] BattleBoxData } } + public bool[] WC6Flags + { + get + { + if (WondercardData < 0 || WondercardFlags < 0) + return null; + + bool[] r = new bool[(WondercardData-WondercardFlags)*8]; + for (int i = 0; i < r.Length; i++) + r[i] = (Data[WondercardFlags + (i>>3)] >> (i&7) & 0x1) == 1; + return r; + } + set + { + if (WondercardData < 0 || WondercardFlags < 0) + return; + if ((WondercardData - WondercardFlags)*8 != value?.Length) + return; + + byte[] data = new byte[value.Length/8]; + for (int i = 0; i < value.Length; i++) + if (value[i]) + data[i>>3] |= (byte)(1 << (i&7)); + + data.CopyTo(Data, WondercardFlags); + Edited = true; + } + } + public WC6 getWC6(int index) + { + if (WondercardData < 0) + return null; + if (index < 0 || index > 24) + return null; + + return new WC6(Data.Skip(WondercardData + index * WC6.Size).Take(WC6.Size).ToArray()); + } + public void setWC6(WC6 wc6, int index) + { + if (WondercardData < 0) + return; + if (index < 0 || index > 24) + return; + + wc6.Data.CopyTo(Data, WondercardData + index * WC6.Size); + + for (int i = 0; i < 24; i++) + if (BitConverter.ToUInt16(Data, WondercardData + i * WC6.Size) == 0) + for (int j = i + 1; j < 24 - i; j++) // Shift everything down + Array.Copy(Data, WondercardData + j * WC6.Size, Data, WondercardData + (j - 1) * WC6.Size, WC6.Size); + + Edited = true; + } + // Writeback Validity public string checkChunkFF() { @@ -854,7 +906,7 @@ public string checkChunkFF() public string getBlockInfoString() { return Blocks.Aggregate("", (current, b) => current + - $"{b.ID.ToString("00")}: {b.Offset.ToString("X5")}-{(b.Offset + b.Length).ToString("X5")}, {b.Length.ToString("X5")}{Environment.NewLine}"); + $"{b.ID.ToString("00")}: {b.Offset.ToString("X5")}-{(b.Offset + b.Length).ToString("X5")}, {b.Length.ToString("X5")}{Environment.NewLine}"); } } } diff --git a/Misc/WC6.cs b/Misc/WC6.cs index a2c9be72a..e9b35121c 100644 --- a/Misc/WC6.cs +++ b/Misc/WC6.cs @@ -4,15 +4,24 @@ namespace PKHeX { - public class WC6 + public partial class WC6 { internal const int Size = 0x108; internal const int SizeFull = 0x310; + internal const uint EonTicketConst = 0x225D73C2; - public byte[] Data; + public readonly byte[] Data; public WC6(byte[] data = null) { Data = data ?? new byte[Size]; + if (Data.Length == SizeFull) + { + Data = Data.Skip(SizeFull - Size).ToArray(); + DateTime now = DateTime.Now; + Year = (uint)(now.Year - 2000); + Month = (uint)now.Month; + Day = (uint)now.Day; + } } // General Card Properties diff --git a/SAV/SAV_Wondercard.cs b/SAV/SAV_Wondercard.cs index 1cc053ae6..1c0c64508 100644 --- a/SAV/SAV_Wondercard.cs +++ b/SAV/SAV_Wondercard.cs @@ -2,7 +2,6 @@ using System.Drawing; using System.IO; using System.Linq; -using System.Text; using System.Windows.Forms; namespace PKHeX @@ -38,12 +37,11 @@ public SAV_Wondercard(byte[] wcdata = null) if (wcdata == null || wcdata.Length != WC6.Size) return; - Array.Copy(wcdata, wondercard_data, wcdata.Length); + Array.Copy(wcdata, wc6.Data, wcdata.Length); loadwcdata(); } - private readonly byte[] sav = (byte[])Main.SAV.Data.Clone(); - private byte[] wondercard_data = new byte[WC6.Size]; - private const uint EonTicketConst = 0x225D73C2; + private readonly SAV6 SAV = new SAV6((byte[])Main.SAV.Data.Clone()); + private WC6 wc6 = new WC6(); private readonly PictureBox[] pba; // Repopulation Functions @@ -54,54 +52,44 @@ private void setBackground(int index, Image bg) pba[i].BackgroundImage = index == i ? bg : null; currentSlot = index; } - private void removeEmptyWC6s() - { - byte[][] ca = new byte[24][]; - for (int i = 0; i < ca.Length; i++) - ca[i] = sav.Skip(Main.SAV.WondercardData + WC6.Size*i).Take(WC6.Size).ToArray(); - - ca = ca.Where(c => BitConverter.ToUInt16(c, 0) > 0).ToArray(); - for (int i = 0; i < ca.Length; i++) - ca[i].CopyTo(sav, Main.SAV.WondercardData + WC6.Size * i); - for (int i = ca.Length; i < 24; i++) - new byte[WC6.Size].CopyTo(sav, Main.SAV.WondercardData + WC6.Size * i); - } private void populateWClist() { - removeEmptyWC6s(); for (int i = 0; i < 24; i++) { - int offset = Main.SAV.WondercardData + i * WC6.Size; - int cardID = BitConverter.ToUInt16(sav, offset); - pba[i].Image = cardID == 0 ? null : getWCPreviewImage(sav.Skip(Main.SAV.WondercardData + WC6.Size * i).Take(WC6.Size).ToArray()); + WC6 wc = SAV.getWC6(i); + pba[i].Image = wc.CardID == 0 ? null : wc.Preview; } } private void loadwcdata() { + if (wc6 == null) + return; try { - if ((wondercard_data[0x52] & 2) != 0 && DialogResult.Yes == + if (wc6.GiftUsed && DialogResult.Yes == Util.Prompt(MessageBoxButtons.YesNo, "Wonder Card is marked as USED and will not be able to be picked up in-game.", "Do you want to remove the USED flag so that it is UNUSED?")) - wondercard_data[0x52] &= 0xFD; // keep all bits but bit1 (11111101) + wc6.GiftUsed = false; - RTB.Text = getWCDescriptionString(wondercard_data); - PB_Preview.Image = getWCPreviewImage(wondercard_data); + RTB.Text = wc6.Description; + PB_Preview.Image = wc6.Preview; } catch (Exception e) { Util.Error("Loading of data failed... is this really a Wonder Card?", e.ToString()); - wondercard_data = new byte[WC6.Size]; + wc6 = new WC6(); RTB.Clear(); } } private void populateReceived() { LB_Received.Items.Clear(); - for (int i = 1; i < 2048; i++) - if ((sav[Main.SAV.WondercardFlags + i / 8]>>i%8 & 0x1) == 1) + bool[] flags = SAV.WC6Flags; + for (int i = 1; i < flags.Length; i++) + if (flags[i]) LB_Received.Items.Add(i.ToString("0000")); + if (LB_Received.Items.Count > 0) LB_Received.SelectedIndex = 0; } @@ -128,17 +116,14 @@ private void B_Import_Click(object sender, EventArgs e) Util.Error("File is not a Wonder Card:", path); return; } - byte[] newwc6 = File.ReadAllBytes(path); - if (newwc6.Length == WC6.SizeFull) - newwc6 = newwc6.Skip(WC6.SizeFull - WC6.Size).ToArray(); - Array.Copy(newwc6, wondercard_data, newwc6.Length); + wc6 = new WC6(File.ReadAllBytes(path)); loadwcdata(); } private void B_Output_Click(object sender, EventArgs e) { SaveFileDialog outputwc6 = new SaveFileDialog(); - int cardID = BitConverter.ToUInt16(wondercard_data, 0); - string cardname = Encoding.Unicode.GetString(wondercard_data, 0x2, 0x48); + int cardID = wc6.CardID; + string cardname = wc6.CardTitle; outputwc6.FileName = Util.CleanFileName($"{cardID} - {cardname}.wc6"); outputwc6.Filter = "Wonder Card|*.wc6"; if (outputwc6.ShowDialog() != DialogResult.OK) return; @@ -148,7 +133,7 @@ private void B_Output_Click(object sender, EventArgs e) if (File.Exists(path)) // File already exists, save a .bak File.WriteAllBytes(path + ".bak", File.ReadAllBytes(path)); - File.WriteAllBytes(path, wondercard_data); + File.WriteAllBytes(path, wc6.Data); } // Wonder Card RW (window<->sav) @@ -158,13 +143,14 @@ private void clickView(object sender, EventArgs e) int index = Array.IndexOf(pba, sender); setBackground(index, Properties.Resources.slotView); - int offset = Main.SAV.WondercardData + index * WC6.Size; - Array.Copy(sav, offset, wondercard_data, 0, WC6.Size); + wc6 = SAV.getWC6(index); loadwcdata(); } private void clickSet(object sender, EventArgs e) { - if (!checkSpecialWonderCard(wondercard_data)) + if (wc6 == null) + return; + if (!checkSpecialWonderCard(wc6)) return; sender = ((sender as ToolStripItem)?.Owner as ContextMenuStrip)?.SourceControl ?? sender as PictureBox; @@ -176,10 +162,9 @@ private void clickSet(object sender, EventArgs e) index = lastUnfilled; setBackground(index, Properties.Resources.slotSet); - int offset = Main.SAV.WondercardData + index * WC6.Size; - Array.Copy(wondercard_data, 0, sav, offset, WC6.Size); + SAV.setWC6(wc6, index); populateWClist(); - setCardID(BitConverter.ToUInt16(wondercard_data, 0)); + setCardID(wc6.CardID); } private void clickDelete(object sender, EventArgs e) { @@ -187,8 +172,7 @@ private void clickDelete(object sender, EventArgs e) int index = Array.IndexOf(pba, sender); setBackground(index, Properties.Resources.slotDel); - int offset = Main.SAV.WondercardData + index * WC6.Size; - new byte[WC6.Size].CopyTo(sav, offset); + SAV.setWC6(new WC6(), index); populateWClist(); } @@ -202,24 +186,12 @@ private void B_Save_Click(object sender, EventArgs e) int offset = Main.SAV.WondercardFlags; // Make sure all of the Received Flags are flipped! - byte[] wcflags = new byte[0x100]; - foreach (uint cardnum in from object card in LB_Received.Items - select card.ToString() into cardID - select Util.ToUInt32(cardID)) - wcflags[(cardnum / 8) & 0xFF] |= (byte)(1 << (byte)(cardnum & 0x7)); + bool[] flags = new bool[(SAV.WondercardData - SAV.WondercardFlags)*8]; + foreach (var o in LB_Received.Items) + flags[Util.ToUInt32(o.ToString())] = true; - Array.Copy(wcflags, 0, sav, offset, 0x100); - - offset += 0x100; - // Make sure there's no space between wondercards - { - for (int i = 0; i < 24; i++) - if (BitConverter.ToUInt16(sav, offset + i * WC6.Size) == 0) - for (int j = i + 1; j < 24 - i; j++) // Shift everything down - Array.Copy(sav, offset + j * WC6.Size, sav, offset + (j - 1) * WC6.Size, WC6.Size); - } - - Array.Copy(sav, Main.SAV.Data, sav.Length); + SAV.WC6Flags = flags; + Main.SAV.Data = SAV.Data; Main.SAV.Edited = true; Close(); } @@ -260,7 +232,7 @@ private void tabMain_DragDrop(object sender, DragEventArgs e) byte[] newwc6 = File.ReadAllBytes(path); if (newwc6.Length == WC6.SizeFull) newwc6 = newwc6.Skip(WC6.SizeFull - WC6.Size).ToArray(); - Array.Copy(newwc6, wondercard_data, newwc6.Length); + Array.Copy(newwc6, wc6.Data, newwc6.Length); loadwcdata(); return; } @@ -279,10 +251,11 @@ private void tabMain_DragDrop(object sender, DragEventArgs e) if (newwc6.Length == WC6.SizeFull) newwc6 = newwc6.Skip(WC6.SizeFull - WC6.Size).ToArray(); - if (checkSpecialWonderCard(newwc6)) + if (checkSpecialWonderCard(new WC6(newwc6))) { - newwc6.CopyTo(sav, Main.SAV.WondercardData + WC6.Size * ctr++); - setCardID(BitConverter.ToUInt16(newwc6, 0)); + WC6 wc = new WC6(newwc6); + SAV.setWC6(wc, ctr++); + setCardID(wc.CardID); } if (ctr >= 24) break; @@ -290,85 +263,43 @@ private void tabMain_DragDrop(object sender, DragEventArgs e) populateWClist(); } - private bool checkSpecialWonderCard(byte[] data) + private bool checkSpecialWonderCard(WC6 wc) { - ushort cardID = BitConverter.ToUInt16(data, 0); - ushort item = BitConverter.ToUInt16(data, 0x68); - if (cardID == 2048 && item == 726) // Eon Ticket (OR/AS) + if (wc6.CardID == 2048 && wc.Item == 726) // Eon Ticket (OR/AS) { if (!Main.SAV.ORAS || Main.SAV.EonTicket < 0) goto reject; - BitConverter.GetBytes(EonTicketConst).CopyTo(sav, Main.SAV.EonTicket); + BitConverter.GetBytes(WC6.EonTicketConst).CopyTo(SAV.Data, Main.SAV.EonTicket); } return true; reject: Util.Alert("Unable to insert the Wonder Card.", "Does this Wonder Card really belong to this game?"); return false; } - - // String Creation - private string getWCDescriptionString(byte[] data) - { - WC6 wc = new WC6(data); - if (wc.CardID == 0) - return "Empty Slot. No data!"; - - string s = $"Card #: {wc.CardID.ToString("0000")} - {wc.CardTitle.Trim()}" + Environment.NewLine; - - switch (wc.CardType) - { - case 1: - s += "Item: " + Main.itemlist[wc.Item] + Environment.NewLine + "Quantity: " + wc.Quantity; - return s; - case 0: - s += - $"{Main.specieslist[wc.Species]} @ {Main.itemlist[wc.HeldItem]} --- {wc.OT} - {wc.TID.ToString("00000")}/{wc.SID.ToString("00000")}" + Environment.NewLine + - $"{Main.movelist[wc.Move1]} / {Main.movelist[wc.Move2]} / {Main.movelist[wc.Move3]} / {Main.movelist[wc.Move4]}" + Environment.NewLine; - return s; - default: - s += "Unknown Wonder Card Type!"; - return s; - } - } - - private Image getWCPreviewImage(byte[] data) - { - Image img; - WC6 wc = new WC6(data); - if (wc.IsPokémon) - img = PKX.getSprite(wc.Species, wc.Form, wc.Gender, wc.HeldItem, wc.IsEgg, wc.PIDType == 2); - else if (wc.IsItem) - img = (Image)(Properties.Resources.ResourceManager.GetObject("item_" + BitConverter.ToUInt16(data, 0x68)) ?? Properties.Resources.unknown); - else - img = Properties.Resources.unknown; - - if (wc.GiftUsed) - img = Util.LayerImage(new Bitmap(img.Width, img.Height), img, 0, 0, 0.3); - return img; - } + private void L_QR_Click(object sender, EventArgs e) { if (ModifierKeys == Keys.Alt) { - byte[] wc = Util.getQRData(); - if (wc == null) return; - if (wc.Length != WC6.Size) { Util.Alert("Decoded data not 0x108 bytes.", - $"QR Data Size: 0x{wc.Length.ToString("X")}"); } + byte[] data = Util.getQRData(); + if (data == null) return; + if (data.Length != WC6.Size) { Util.Alert("Decoded data not 0x108 bytes.", + $"QR Data Size: 0x{data.Length.ToString("X")}"); } else try { - wc.CopyTo(wondercard_data, 0); + wc6 = new WC6(data); loadwcdata(); } catch { Util.Alert("Error loading wondercard data."); } } else { - if (wondercard_data.SequenceEqual(new byte[wondercard_data.Length])) + if (wc6.Data.SequenceEqual(new byte[wc6.Data.Length])) { Util.Alert("No wondercard data found in loaded slot!"); return; } - if (BitConverter.ToUInt16(wondercard_data, 0x68) == 726 && wondercard_data[0x51] == 1) + if (wc6.Item == 726 && wc6.IsItem) { Util.Alert("Eon Ticket Wonder Cards will not function properly", "Inject to the save file instead."); return; } // Prep data - byte[] wcdata = wondercard_data; + byte[] wcdata = wc6.Data; // Ensure size Array.Resize(ref wcdata, WC6.Size); // Setup QR @@ -376,7 +307,7 @@ private void L_QR_Click(object sender, EventArgs e) Image qr = Util.getQRImage(wcdata, server); if (qr == null) return; - string desc = getWCDescriptionString(wondercard_data); + string desc = wc6.Description; new QR(qr, PB_Preview.Image, desc, "", "", "PKHeX Wonder Card @ ProjectPokemon.org").ShowDialog(); } @@ -407,15 +338,14 @@ private void pbBoxSlot_MouseDown(object sender, MouseEventArgs e) Cursor.Current = Cursors.Hand; // Prepare Data - byte[] dragdata = sav.Skip(Main.SAV.WondercardData + WC6.Size * index).Take(WC6.Size).ToArray(); - WC6 card = new WC6(dragdata); + WC6 card = SAV.getWC6(index); string filename = Util.CleanFileName($"{card.CardID.ToString("0000")} - {card.CardTitle}.wc6"); // Make File string newfile = Path.Combine(Path.GetTempPath(), Util.CleanFileName(filename)); try { - File.WriteAllBytes(newfile, dragdata); + File.WriteAllBytes(newfile, card.Data); DoDragDrop(new DataObject(DataFormats.FileDrop, new[] { newfile }), DragDropEffects.Move); } catch (ArgumentException x) @@ -440,22 +370,22 @@ private void pbBoxSlot_DragDrop(object sender, DragEventArgs e) if (files.Length < 1 || new FileInfo(files[0]).Length != WC6.Size) return; - - byte[] wcdata = File.ReadAllBytes(files[0]); - wcdata.CopyTo(sav, Main.SAV.WondercardData + WC6.Size * index); - wcdata.CopyTo(wondercard_data, 0); + + WC6 wc = new WC6(File.ReadAllBytes(files[0])); + SAV.setWC6(wc, index); + setCardID(wc.CardID); + wc6 = wc; loadwcdata(); - setCardID(BitConverter.ToUInt16(wcdata, 0)); } else // Swap Data { // Check to see if they copied beyond blank slots. if (index > Math.Max(wc_slot, lastUnfilled - 1)) index = Math.Max(wc_slot, lastUnfilled - 1); - byte[] s1 = sav.Skip(Main.SAV.WondercardData + WC6.Size * index).Take(WC6.Size).ToArray(); - byte[] s2 = sav.Skip(Main.SAV.WondercardData + WC6.Size * wc_slot).Take(WC6.Size).ToArray(); - s1.CopyTo(sav, Main.SAV.WondercardData + WC6.Size * wc_slot); - s2.CopyTo(sav, Main.SAV.WondercardData + WC6.Size * index); + WC6 s1 = SAV.getWC6(index); + WC6 s2 = SAV.getWC6(wc_slot); + SAV.setWC6(s1, wc_slot); + SAV.setWC6(s2, index); } setBackground(index, Properties.Resources.slotView); populateWClist(); @@ -469,4 +399,51 @@ private void pbBoxSlot_DragEnter(object sender, DragEventArgs e) } private int wc_slot = -1; } + + // Extension Properties + public partial class WC6 + { + public Image Preview + { + get + { + Image img; + if (IsPokémon) + img = PKX.getSprite(Species, Form, Gender, HeldItem, IsEgg, PIDType == 2); + else if (IsItem) + img = (Image)(Properties.Resources.ResourceManager.GetObject("item_" + Item) ?? Properties.Resources.unknown); + else + img = Properties.Resources.unknown; + + if (GiftUsed) + img = Util.LayerImage(new Bitmap(img.Width, img.Height), img, 0, 0, 0.3); + return img; + } + } + public string Description + { + get + { + if (CardID == 0) + return "Empty Slot. No data!"; + + string s = $"Card #: {CardID.ToString("0000")} - {CardTitle.Trim()}" + Environment.NewLine; + + switch (CardType) + { + case 1: + s += "Item: " + Main.itemlist[Item] + Environment.NewLine + "Quantity: " + Quantity; + return s; + case 0: + s += + $"{Main.specieslist[Species]} @ {Main.itemlist[HeldItem]} --- {OT} - {TID.ToString("00000")}/{SID.ToString("00000")}" + Environment.NewLine + + $"{Main.movelist[Move1]} / {Main.movelist[Move2]} / {Main.movelist[Move3]} / {Main.movelist[Move4]}" + Environment.NewLine; + return s; + default: + s += "Unknown Wonder Card Type!"; + return s; + } + } + } + } } \ No newline at end of file