From e7fc30ac7a6d335aa3cb377f514f887b74b5b2a4 Mon Sep 17 00:00:00 2001 From: Kurt Date: Tue, 5 Jun 2018 18:27:08 -0700 Subject: [PATCH] Add mysterygift region/language restriction code Closes #1317 Needs the events crew to produce serialized binaries with hash-flag data for each generation before proceeding any further. I'm not really interested in doing all the work for events since it doesn't impact battle legality. --- PKHeX.Core/Game/RegionID.cs | 13 +++ .../Verifiers/MysteryGiftRestriction.cs | 54 ++++++++++ .../Verifiers/MysteryGiftVerifier.cs | 102 ++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 PKHeX.Core/Game/RegionID.cs create mode 100644 PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftRestriction.cs create mode 100644 PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs diff --git a/PKHeX.Core/Game/RegionID.cs b/PKHeX.Core/Game/RegionID.cs new file mode 100644 index 000000000..7e4968367 --- /dev/null +++ b/PKHeX.Core/Game/RegionID.cs @@ -0,0 +1,13 @@ +namespace PKHeX.Core +{ + public enum RegionID + { + None = 0, + RegJP = 1, + RegNA = 2, + RegEU = 3, + RegZH = 4, + RegKO = 5, + RegTW = 6, + } +} diff --git a/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftRestriction.cs b/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftRestriction.cs new file mode 100644 index 000000000..f27d1ccdc --- /dev/null +++ b/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftRestriction.cs @@ -0,0 +1,54 @@ +using System; + +namespace PKHeX.Core +{ + [Flags] + public enum MysteryGiftRestriction : ushort + { + None = 0, + LangJapanese = 1 << LanguageID.Japanese, + LangEnglish = 1 << LanguageID.English, + LangFrench = 1 << LanguageID.French, + LangItalian = 1 << LanguageID.Italian, + LangGerman = 1 << LanguageID.German, + LangSpanish = 1 << LanguageID.Spanish, + LangKorean = 1 << LanguageID.Korean, + + LangRestrict = LangJapanese | LangEnglish | LangFrench | LangItalian | LangGerman | LangSpanish | LangKorean, + RegionBase = LangKorean, + + RegJP = RegionBase << RegionID.Japan, + RegNA = RegionBase << RegionID.NorthAmerica, + RegEU = RegionBase << RegionID.Europe, + RegZH = RegionBase << RegionID.China, + RegKO = RegionBase << RegionID.Korea, + RegTW = RegionBase << RegionID.Taiwan, + + RegionRestrict = RegJP | RegNA | RegEU | RegZH | RegKO | RegTW, + + OTReplacedOnTrade = RegTW << 1, + } + + public static class MysteryGiftRestrictionExtensions + { + public static bool HasFlagFast(this MysteryGiftRestriction value, MysteryGiftRestriction flag) + { + return (value & flag) != 0; + } + + public static int GetSuggestedLanguage(this MysteryGiftRestriction value) + { + for (int i = (int)LanguageID.Japanese; i <= (int)LanguageID.Korean; i++) + if (value.HasFlagFast((MysteryGiftRestriction)(1 << i))) + return i; + return -1; + } + public static int GetSuggestedRegion(this MysteryGiftRestriction value) + { + for (int i = (int)RegionID.Japan; i <= (int)RegionID.Taiwan; i++) + if (value.HasFlagFast((MysteryGiftRestriction)((int)MysteryGiftRestriction.RegionBase << i))) + return i; + return -1; + } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs b/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs new file mode 100644 index 000000000..c5303bce9 --- /dev/null +++ b/PKHeX.Core/Legality/Encounters/Verifiers/MysteryGiftVerifier.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using static PKHeX.Core.LegalityCheckStrings; + +namespace PKHeX.Core +{ + public static class MysteryGiftVerifier + { + private static readonly Dictionary[] RestrictionSet = Get(); + private static Dictionary[] Get() + { + var s = new Dictionary[PKX.Generation + 1]; + for (int i = 3; i < s.Length; i++) + s[i] = GetRestriction(i); + return s; + } + + private static string RestrictionSetName(int i) => $"mgrestrict{i}.pkl"; + private static Dictionary GetRestriction(int generation) + { + var resource = RestrictionSetName(generation); + var data = Util.GetBinaryResource(resource); + var dict = new Dictionary(); + for (int i = 0; i < data.Length; i += 4 + 2) + { + int hash = BitConverter.ToInt32(data, i + 0); + var restrict = BitConverter.ToUInt16(data, i + 4); + dict.Add(hash, (MysteryGiftRestriction)restrict); + } + return dict; + } + + public static CheckResult VerifyGift(PKM pk, MysteryGift g) + { + bool restricted = TryGetRestriction(g, out var val); + if (!restricted) + return new CheckResult(CheckIdentifier.GameOrigin); + + var lang = val & MysteryGiftRestriction.LangRestrict; + if (lang != 0) + { + var current = 1 << pk.Language; + if (!lang.HasFlagFast((MysteryGiftRestriction) current)) + { + int suggest = lang.GetSuggestedLanguage(); + return new CheckResult(Severity.Invalid, string.Format(V5, suggest, pk.Language), CheckIdentifier.GameOrigin); + } + } + var region = val & MysteryGiftRestriction.RegionRestrict; + if (region != 0) + { + var current = (int)MysteryGiftRestriction.RegionBase << pk.ConsoleRegion; + if (!region.HasFlagFast((MysteryGiftRestriction)current)) + return new CheckResult(Severity.Invalid, V301, CheckIdentifier.GameOrigin); + } + + return new CheckResult(CheckIdentifier.GameOrigin); + } + + private static bool TryGetRestriction(MysteryGift g, out MysteryGiftRestriction val) + { + var restrict = RestrictionSet[g.Generation]; + if (restrict != null) + return restrict.TryGetValue(g.GetHashCode(), out val); + val = MysteryGiftRestriction.None; + return false; + } + + public static bool IsValidChangedOTName(PKM pk, MysteryGift g) + { + bool restricted = TryGetRestriction(g, out var val); + if (!restricted) + return false; // no data + if (!val.HasFlagFast(MysteryGiftRestriction.OTReplacedOnTrade)) + return false; + return CurrentOTMatchesReplaced(g.Format, pk.OT_Name); + } + + private static bool CurrentOTMatchesReplaced(int format, string pkOtName) + { + if (format <= 4 && IsMatchName(pkOtName, 4)) + return true; + if (format <= 5 && IsMatchName(pkOtName, 5)) + return true; + if (format <= 6 && IsMatchName(pkOtName, 6)) + return true; + if (format <= 7 && IsMatchName(pkOtName, 7)) + return true; + return false; + } + + private static bool IsMatchName(string pkOtName, int generation) + { + switch (generation) + { + // todo + default: + return false; + } + } + } +}