diff --git a/PKHeX.Core/Legality/Checks.cs b/PKHeX.Core/Legality/Checks.cs index 9b7638a48..9d8955865 100644 --- a/PKHeX.Core/Legality/Checks.cs +++ b/PKHeX.Core/Legality/Checks.cs @@ -85,10 +85,14 @@ namespace PKHeX.Core case EncounterStatic s: if (s.Shiny != null && (bool)s.Shiny ^ pkm.IsShiny) AddLine(Severity.Invalid, V209, CheckIdentifier.Shiny); + if (pkm.GenNumber == 5 && !s.Gift && !s.Roaming && s.Ability != 4) + VerifyG5PID_IDCorrelation(); break; case EncounterSlot w: if (pkm.IsShiny && w.Type == SlotType.HiddenGrotto) AddLine(Severity.Invalid, V221, CheckIdentifier.Shiny); + if (pkm.GenNumber == 5 && pkm.AbilityNumber != 4) + VerifyG5PID_IDCorrelation(); break; case PCD d: // fixed PID if (d.Gift.PK.PID != 1 && pkm.EncryptionConstant != d.Gift.PK.PID) @@ -96,6 +100,13 @@ namespace PKHeX.Core break; } } + private void VerifyG5PID_IDCorrelation() + { + var pid = pkm.EncryptionConstant; + var result = (pid & 1) ^ (pid >> 31) ^ (pkm.TID & 1) ^ (pkm.SID & 1); + if (result != 0) + AddLine(Severity.Invalid, V411, CheckIdentifier.PID); + } private void VerifyECPIDWurmple() { uint evoVal = PKX.GetWurmpleEvoVal(pkm.EncryptionConstant); @@ -783,7 +794,7 @@ namespace PKHeX.Core var c3 = u4.RibbonBitsContest3(); var c3n = u4.RibbonNamesContest3(); var c4 = u4.RibbonBitsContest4(); var c4n = u4.RibbonNamesContest4(); var iter3 = gen == 3 ? getMissingContestRibbons(c3, c3n) : GetRibbonMessageNone(c3, c3n); - var iter4 = gen == 4 && IsAllowedInContest4(pkm.Species) ? getMissingContestRibbons(c4, c4n) : GetRibbonMessageNone(c4, c4n); + var iter4 = (gen == 3 || gen == 4) && IsAllowedInContest4(pkm.Species) ? getMissingContestRibbons(c4, c4n) : GetRibbonMessageNone(c4, c4n); foreach (var z in iter3.Concat(iter4)) yield return z; diff --git a/PKHeX.Core/Legality/Structures/EvolutionTree.cs b/PKHeX.Core/Legality/Structures/EvolutionTree.cs index 36d5b2dd6..b997aa41a 100644 --- a/PKHeX.Core/Legality/Structures/EvolutionTree.cs +++ b/PKHeX.Core/Legality/Structures/EvolutionTree.cs @@ -165,15 +165,15 @@ namespace PKHeX.Core var evo1 = raichu1.Chain[0].StageEntryMethods[0].Copy(); Lineage[26].Chain.Add(new EvolutionStage { StageEntryMethods = new List { evo1 } }); var evo2 = raichu1.Chain[1].StageEntryMethods[0].Copy(); - evo2.Form = -1; evo2.Banlist = new[] { GameVersion.SN, GameVersion.MN }; + evo2.Form = -1; evo2.Banlist = EvolutionMethod.BanSM; Lineage[26].Chain.Add(new EvolutionStage { StageEntryMethods = new List { evo2 } }); var exegg = Lineage[Personal.GetFormeIndex(103, 1)].Chain[0].StageEntryMethods[0].Copy(); - exegg.Form = -1; exegg.Banlist = new[] { GameVersion.SN, GameVersion.MN }; exegg.Method = 8; // No night required (doesn't matter) + exegg.Form = -1; exegg.Banlist = EvolutionMethod.BanSM; exegg.Method = 8; // No night required (doesn't matter) Lineage[103].Chain.Add(new EvolutionStage { StageEntryMethods = new List { exegg } }); var marowak = Lineage[Personal.GetFormeIndex(105, 1)].Chain[0].StageEntryMethods[0].Copy(); - marowak.Form = -1; marowak.Banlist = new[] {GameVersion.SN, GameVersion.MN}; + marowak.Form = -1; marowak.Banlist = EvolutionMethod.BanSM; Lineage[105].Chain.Add(new EvolutionStage { StageEntryMethods = new List { marowak } }); } @@ -512,8 +512,11 @@ namespace PKHeX.Core public int Level; public bool RequiresLevelUp; - public static readonly int[] TradeMethods = {5, 6, 7}; - public GameVersion[] Banlist = new GameVersion[0]; + + internal static readonly int[] TradeMethods = {5, 6, 7}; + private static readonly IReadOnlyCollection NoBanlist = new GameVersion[0]; + internal static readonly IReadOnlyCollection BanSM = new[] {GameVersion.SN, GameVersion.MN}; + internal IReadOnlyCollection Banlist = NoBanlist; public bool Valid(PKM pkm, int lvl, bool skipChecks) { diff --git a/PKHeX.Core/Saves/SAV6.cs b/PKHeX.Core/Saves/SAV6.cs index 857935776..4ed845e8a 100644 --- a/PKHeX.Core/Saves/SAV6.cs +++ b/PKHeX.Core/Saves/SAV6.cs @@ -431,8 +431,8 @@ namespace PKHeX.Core } public bool IsMegaEvolutionUnlocked { - get => Data[TrainerCard + 0x14A] == 1; - set => Data[TrainerCard + 0x14A] = (byte)(value ? 1 : 0); + get => (Data[TrainerCard + 0x14A] & 0x01) != 0; + set => Data[TrainerCard + 0x14A] = (byte)((Data[TrainerCard + 0x14A] & 0xFE) | (value ? 1 : 0)); // in battle } public int M diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index a210acacb..e617b4b6a 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -1305,8 +1305,8 @@ namespace PKHeX.Core } public bool MegaUnlocked { - get => (Data[0x1278] & 0x01) != 0; - set => Data[0x1278] = (byte)((Data[0x1278] & 0xFE) | (value ? 1 : 0)); // in battle + get => (Data[TrainerCard + 0x78] & 0x01) != 0; + set => Data[TrainerCard + 0x78] = (byte)((Data[TrainerCard + 0x78] & 0xFE) | (value ? 1 : 0)); // in battle // Data[0x1F22] = (byte)((Data[0x1F22] & 0xFE) | (value ? 1 : 0)); // event } diff --git a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs index c6232a023..5cc545f66 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs @@ -423,19 +423,27 @@ namespace PKHeX.WinForms.Controls PB_MarkVC.Image = ImageUtil.ChangeOpacity(PB_MarkVC.InitialImage, getOpacity(pkm.VC)); PB_MarkHorohoro.Image = ImageUtil.ChangeOpacity(PB_MarkHorohoro.InitialImage, getOpacity(pkm.Horohoro)); + var markings = pkm.Markings; for (int i = 0; i < pba.Length; i++) + if (GetMarkingColor(markings[i], out Color c)) + pba[i].Image = ImageUtil.ChangeAllColorTo(pba[i].Image, c); + } + private static bool GetMarkingColor(int markval, out Color c) + { + switch (markval) { - switch (pkm.Markings[i]) - { - case 1: - pba[i].Image = ImageUtil.ChangeAllColorTo(pba[i].Image, Color.FromArgb(000, 191, 255)); - break; - case 2: - pba[i].Image = ImageUtil.ChangeAllColorTo(pba[i].Image, Color.FromArgb(255, 117, 179)); - break; - } + case 1: + c = Color.FromArgb(000, 191, 255); + return true; + case 2: + c = Color.FromArgb(255, 117, 179); + return true; + default: + c = Color.Black; + return false; } } + private void UpdateGender() { int cg = PKX.GetGender(Label_Gender.Text); @@ -1281,22 +1289,26 @@ namespace PKHeX.WinForms.Controls CB_MetLocation.ValueMember = "Value"; CB_MetLocation.DataSource = new BindingSource(met_list, null); - int metLoc = 0; // transporter or pal park for past gen pkm - switch (newTrack) + if (fieldsLoaded) { - case GameVersion.GO: metLoc = 30012; break; - case GameVersion.RBY: metLoc = 30013; break; + int metLoc = 0; // transporter or pal park for past gen pkm + switch (newTrack) + { + case GameVersion.GO: metLoc = 30012; break; + case GameVersion.RBY: metLoc = 30013; break; + } + if (metLoc != 0) + CB_MetLocation.SelectedValue = metLoc; + else + CB_MetLocation.SelectedIndex = metLoc; } - if (metLoc != 0) - CB_MetLocation.SelectedValue = metLoc; - else - CB_MetLocation.SelectedIndex = metLoc; var egg_list = GameInfo.GetLocationList(Version, pkm.Format, egg: true); CB_EggLocation.DisplayMember = "Text"; CB_EggLocation.ValueMember = "Value"; CB_EggLocation.DataSource = new BindingSource(egg_list, null); - CB_EggLocation.SelectedIndex = CHK_AsEgg.Checked ? 1 : 0; // daycare : none + if (fieldsLoaded) + CB_EggLocation.SelectedIndex = CHK_AsEgg.Checked ? 1 : 0; // daycare : none origintrack = newTrack; @@ -1305,6 +1317,9 @@ namespace PKHeX.WinForms.Controls if (Version == GameVersion.CXD && pkm.Format == 3) width = 2 * width; CB_MetLocation.DropDownWidth = width; + + if (!fieldsLoaded) + ValidateChildren(); // prevent value resetting when finishing load routine } // Visibility logic for Gen 4 encounter type; only show for Gen 4 Pokemon. @@ -1726,7 +1741,7 @@ namespace PKHeX.WinForms.Controls { if (e.Index < 0) return; - var i = (ComboItem)(sender as ComboBox).Items[e.Index]; + var i = (ComboItem)((ComboBox)sender).Items[e.Index]; var moves = Legality.AllSuggestedMovesAndRelearn; bool vm = moves != null && moves.Contains(i.Value) && !HaX; diff --git a/PKHeX.WinForms/Misc/QR.cs b/PKHeX.WinForms/Misc/QR.cs index c61848e5e..514ff7514 100644 --- a/PKHeX.WinForms/Misc/QR.cs +++ b/PKHeX.WinForms/Misc/QR.cs @@ -79,52 +79,61 @@ namespace PKHeX.WinForms // QR Utility private const string QR6PathBad = "null/#"; // prefix to prevent URL from loading private const string QR6Path = @"http://lunarcookies.github.io/b1s1.html#"; + private const string DecodeAPI = "http://api.qrserver.com/v1/read-qr-code/?fileurl="; + private const int QRSize = 365; + private static readonly string EncodeAPI = $"http://chart.apis.google.com/chart?chs={QRSize}x{QRSize}&cht=qr&chl="; internal static byte[] GetQRData(string address) { // Fetch data from QR code... - try { if (address.Length < 4 || address.Substring(0, 3) != "htt") { WinFormsUtil.Alert("Clipboard text is not a valid URL:", address); return null; } } + try { if (address.Length < 4 || !address.StartsWith("http")) { WinFormsUtil.Alert("Clipboard text is not a valid URL:", address); return null; } } catch { WinFormsUtil.Alert("Clipboard text is not a valid URL:", address); return null; } - string webURL = "http://api.qrserver.com/v1/read-qr-code/?fileurl=" + HttpUtility.UrlEncode(address); + string webURL = DecodeAPI + HttpUtility.UrlEncode(address); + string data; try { - string data = NetUtil.GetStringFromURL(webURL); + data = NetUtil.GetStringFromURL(webURL); if (data.Contains("could not find")) { WinFormsUtil.Alert("Reader could not find QR data in the image."); return null; } if (data.Contains("filetype not supported")) { WinFormsUtil.Alert("Input URL is not valid. Double check that it is an image (jpg/png).", address); return null; } - // Quickly convert the json response to a data string - const string cap = "\",\"error\":null}]}]"; - const string intro = "[{\"type\":\"qrcode\",\"symbol\":[{\"seq\":0,\"data\":\""; - if (!data.StartsWith(intro)) - throw new FormatException(); - - string pkstr = data.Substring(intro.Length); - if (pkstr.Contains("nQR-Code:")) // Remove multiple QR codes in same image - pkstr = pkstr.Substring(0, pkstr.IndexOf("nQR-Code:", StringComparison.Ordinal)); - pkstr = pkstr.Substring(0, pkstr.IndexOf(cap, StringComparison.Ordinal)); // Trim outro - try - { - if (!pkstr.StartsWith("http") && !pkstr.StartsWith(QR6PathBad)) // G7 - { - string fstr = Regex.Unescape(pkstr); - byte[] raw = Encoding.Unicode.GetBytes(fstr); - // Remove 00 interstitials and retrieve from offset 0x30, take PK7 Stored Size (always) - return raw.ToList().Where((c, i) => i % 2 == 0).Skip(0x30).Take(0xE8).ToArray(); - } - // All except G7 - pkstr = pkstr.Substring(pkstr.IndexOf("#", StringComparison.Ordinal) + 1); // Trim URL - pkstr = pkstr.Replace("\\", ""); // Rectify response - - return Convert.FromBase64String(pkstr); - } - catch { WinFormsUtil.Alert("QR string to Data failed."); return null; } } catch { WinFormsUtil.Alert("Unable to connect to the internet to decode QR code."); return null; } + + // Quickly convert the json response to a data string + try { return DecodeQRJson(data); } + catch (Exception e) { WinFormsUtil.Alert("QR string to Data failed.", e.Message); return null; } } + private static byte[] DecodeQRJson(string data) + { + const string cap = "\",\"error\":null}]}]"; + const string intro = "[{\"type\":\"qrcode\",\"symbol\":[{\"seq\":0,\"data\":\""; + const string qrcode = "nQR-Code:"; + if (!data.StartsWith(intro)) + throw new FormatException(); + + string pkstr = data.Substring(intro.Length); + if (pkstr.Contains(qrcode)) // Remove multiple QR codes in same image + pkstr = pkstr.Substring(0, pkstr.IndexOf(qrcode, StringComparison.Ordinal)); + pkstr = pkstr.Substring(0, pkstr.IndexOf(cap, StringComparison.Ordinal)); // Trim outro + + if (!pkstr.StartsWith("http") && !pkstr.StartsWith(QR6PathBad)) // G7 + { + string fstr = Regex.Unescape(pkstr); + byte[] raw = Encoding.Unicode.GetBytes(fstr); + // Remove 00 interstitials and retrieve from offset 0x30, take PK7 Stored Size (always) + return raw.ToList().Where((c, i) => i % 2 == 0).Skip(0x30).Take(0xE8).ToArray(); + } + // All except G7 + pkstr = pkstr.Substring(pkstr.IndexOf("#", StringComparison.Ordinal) + 1); // Trim URL + pkstr = pkstr.Replace("\\", ""); // Rectify response + + return Convert.FromBase64String(pkstr); + } + internal static Image GetQRImage(byte[] data, string server) { string qrdata = Convert.ToBase64String(data); string message = server + qrdata; - string webURL = "http://chart.apis.google.com/chart?chs=365x365&cht=qr&chl=" + HttpUtility.UrlEncode(message); + string webURL = EncodeAPI + HttpUtility.UrlEncode(message); try { @@ -159,12 +168,9 @@ namespace PKHeX.WinForms public static Image GenerateQRCode7(PK7 pk7, int box = 0, int slot = 0, int num_copies = 1) { byte[] data = QR7.GenerateQRData(pk7, box, slot, num_copies); - using (var generator = new QRCodeGenerator()) - using (var qr_data = generator.CreateQRCode(data)) - using (var qr_code = new QRCode(qr_data)) - return qr_code.GetGraphic(4); + return GenerateQRCode(data, ppm: 4); } - public static Image GenerateQRCode(byte[] data, int ppm = 4) + private static Image GenerateQRCode(byte[] data, int ppm = 4) { using (var generator = new QRCodeGenerator()) using (var qr_data = generator.CreateQRCode(data)) diff --git a/PKHeX.WinForms/Util/ImageUtil.cs b/PKHeX.WinForms/Util/ImageUtil.cs index 7c9d39c06..6220baaa4 100644 --- a/PKHeX.WinForms/Util/ImageUtil.cs +++ b/PKHeX.WinForms/Util/ImageUtil.cs @@ -16,7 +16,7 @@ namespace PKHeX.WinForms using (Graphics gr = Graphics.FromImage(img)) { gr.DrawImage(baseLayer, new Point(0, 0)); - Bitmap o = ChangeOpacity(overLayer, trans); + Image o = trans == 1f ? overLayer : ChangeOpacity(overLayer, trans); gr.DrawImage(o, new Rectangle(x, y, overLayer.Width, overLayer.Height)); } return img;