Merge pull request #10 from kwsch/master

Update from upstream repo kwsch/PKHeX
This commit is contained in:
ReignOfComputer 2017-02-26 23:03:04 +08:00 committed by GitHub
commit 1b1e0855fd
90 changed files with 2758 additions and 1145 deletions

1
.gitignore vendored
View file

@ -40,6 +40,7 @@ local.properties
*.suo
*.user
*.sln.docstates
*.vs
# Build results

View file

@ -926,6 +926,7 @@
this.Label_CurLevel.TabIndex = 7;
this.Label_CurLevel.Text = "Level:";
this.Label_CurLevel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.Label_CurLevel.Click += new System.EventHandler(this.clickMetLocation);
//
// TB_Level
//

View file

@ -320,14 +320,14 @@ namespace PKHeX.WinForms
string supported = string.Join(";", SAV.PKMExtensions.Select(s => "*."+s).Concat(new[] {"*.pkm"}));
OpenFileDialog ofd = new OpenFileDialog
{
Filter = $"Supported Files|main;*.sav;*.bin;*.{ekx};{supported};*.bak" +
Filter = "All Files|*.*" +
$"|Supported Files|main;*.sav;*.dat;*.gci;*.bin;*.{ekx};{supported};*.bak" +
"|3DS Main Files|main" +
"|Save Files|*.sav" +
"|Save Files|*.sav;*.dat;*.gci" +
$"|Decrypted PKM File|{supported}" +
$"|Encrypted PKM File|*.{ekx}" +
"|Binary File|*.bin" +
"|Backup File|*.bak" +
"|All Files|*.*"
"|Backup File|*.bak"
};
// Detect main
@ -691,7 +691,9 @@ namespace PKHeX.WinForms
string ext = Path.GetExtension(path);
FileInfo fi = new FileInfo(path);
if (fi.Length > 0x10009C && fi.Length != 0x380000)
WinFormsUtil.Error("Input file is too large.", path);
WinFormsUtil.Error("Input file is too large." + Environment.NewLine + $"Size: {fi.Length} bytes", path);
else if (fi.Length < 32)
WinFormsUtil.Error("Input file is too small." + Environment.NewLine + $"Size: {fi.Length} bytes", path);
else
{
byte[] input; try { input = File.ReadAllBytes(path); }
@ -844,52 +846,22 @@ namespace PKHeX.WinForms
}
private bool openXOR(byte[] input, string path)
{
// Detection of stored Decryption XORpads:
if (ModifierKeys == Keys.Control) return false; // no xorpad compatible
byte[] savID = input.Take(0x10).ToArray();
string exepath = Application.StartupPath;
string xorpath = exepath.Clone().ToString();
string[] XORpads = Directory.GetFiles(xorpath);
// try to get a save file via xorpad in same folder
string[] pads = Directory.GetFiles(path);
var s = SaveUtil.getSAVfromXORpads(input, pads);
int loop = 0;
while (xorpath == exepath && loop++ == 0)
if (s == null) // failed to find xorpad in path folder
{
foreach (byte[] data in from file in XORpads let fi = new FileInfo(file) where (fi.Name.ToLower().Contains("xorpad") || fi.Name.ToLower().Contains("key")) && (fi.Length == 0x10009C || fi.Length == 0x100000) select File.ReadAllBytes(file))
{
// Fix xorpad alignment
byte[] xorpad = data;
if (xorpad.Length == 0x10009C) // Trim off Powersaves' header
xorpad = xorpad.Skip(0x9C).ToArray(); // returns 0x100000
if (!xorpad.Take(0x10).SequenceEqual(savID)) continue;
// Set up Decrypted File
byte[] decryptedPS = input.Skip(0x5400).Take(SaveUtil.SIZE_G6ORAS).ToArray();
// xor through and decrypt
for (int z = 0; z < decryptedPS.Length; z++)
decryptedPS[z] ^= xorpad[0x5400 + z];
// Weakly check the validity of the decrypted content
if (BitConverter.ToUInt32(decryptedPS, SaveUtil.SIZE_G6ORAS - 0x1F0) == SaveUtil.BEEF)
Array.Resize(ref decryptedPS, SaveUtil.SIZE_G6ORAS); // set to ORAS size
else if (BitConverter.ToUInt32(decryptedPS, SaveUtil.SIZE_G6XY - 0x1F0) == SaveUtil.BEEF)
Array.Resize(ref decryptedPS, SaveUtil.SIZE_G6XY); // set to X/Y size
else if (BitConverter.ToUInt32(decryptedPS, SaveUtil.SIZE_G7SM - 0x1F0) == SaveUtil.BEEF)
Array.Resize(ref decryptedPS, SaveUtil.SIZE_G7SM); // set to S/M size
else
continue;
// Save file is now decrypted!
// Trigger Loading of the decrypted save file.
openSAV(SaveUtil.getVariantSAV(decryptedPS), path);
return true;
}
// End file check loop, check the input path for xorpads too if it isn't the same as the EXE (quite common).
xorpath = Path.GetDirectoryName(path); // try again in the next folder up
// try again
pads = Directory.GetFiles(WorkingDirectory);
s = SaveUtil.getSAVfromXORpads(input, pads);
}
return false; // no xorpad compatible
if (s == null)
return false; // failed
openSAV(s, s.FileName);
return true;
}
private void openSAV(SaveFile sav, string path)
{
@ -943,6 +915,8 @@ namespace PKHeX.WinForms
}
// clean fields
bool WindowToggleRequired = SAV.Generation < 3 && sav.Generation >= 3; // version combobox refresh hack
bool WindowTranslationRequired = false;
PKM pk = preparePKM();
populateFields(SAV.BlankPKM);
SAV = sav;
@ -987,8 +961,6 @@ namespace PKHeX.WinForms
}
setPKXBoxes(); // Reload all of the PKX Windows
bool WindowTranslationRequired = false;
// Hide content if not present in game.
GB_SUBE.Visible = SAV.HasSUBE;
PB_Locked.Visible = SAV.HasBattleBox && SAV.BattleBoxLocked;
@ -1231,7 +1203,17 @@ namespace PKHeX.WinForms
PKMConverter.updateConfig(SAV.SubRegion, SAV.Country, SAV.ConsoleRegion, SAV.OT, SAV.Gender, SAV.Language);
if (WindowTranslationRequired) // force update -- re-added controls may be untranslated
{
// Keep window title
title = Text;
WinFormsUtil.TranslateInterface(this, curlanguage);
Text = title;
}
if (WindowToggleRequired) // Version combobox selectedvalue needs a little help, only updates once it is visible
{
tabMain.SelectedTab = Tab_Met; // parent tab of CB_GameOrigin
tabMain.SelectedTab = Tab_Main; // first tab
}
// No changes made yet
UndoStack.Clear(); Menu_Undo.Enabled = false;
@ -1413,11 +1395,11 @@ namespace PKHeX.WinForms
CB_GameOrigin.DataSource = new BindingSource(GameInfo.VersionDataSource.Where(g => g.Value <= SAV.MaxGameID || SAV.Generation >= 3 && g.Value == 15).ToList(), null);
// Set the Move ComboBoxes too..
var moves = (HaX ? GameInfo.HaXMoveDataSource : GameInfo.MoveDataSource).Where(m => m.Value <= SAV.MaxMoveID).ToList(); // Filter Z-Moves if appropriate
GameInfo.MoveDataSource = (HaX ? GameInfo.HaXMoveDataSource : GameInfo.LegalMoveDataSource).Where(m => m.Value <= SAV.MaxMoveID).ToList(); // Filter Z-Moves if appropriate
foreach (ComboBox cb in new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4, CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 })
{
cb.DisplayMember = "Text"; cb.ValueMember = "Value";
cb.DataSource = new BindingSource(moves, null);
cb.DataSource = new BindingSource(GameInfo.MoveDataSource, null);
}
}
private Action getFieldsfromPKM;
@ -1960,8 +1942,12 @@ namespace PKHeX.WinForms
return;
pkm = preparePKM();
updateLegality();
if (Legality.Valid)
return;
var encounter = Legality.getSuggestedMetInfo();
if (encounter == null || encounter.Location < 0)
if (encounter == null || (pkm.Format >= 3 && encounter.Location < 0))
{
WinFormsUtil.Alert("Unable to provide a suggestion.");
return;
@ -1970,21 +1956,37 @@ namespace PKHeX.WinForms
int level = encounter.Level;
int location = encounter.Location;
int minlvl = Legal.getLowestLevel(pkm, encounter.Species);
if (pkm.Met_Level == level && pkm.Met_Location == location && pkm.CurrentLevel >= minlvl)
if (minlvl == 0)
minlvl = level;
if (pkm.CurrentLevel >= minlvl && pkm.Met_Level == level && pkm.Met_Location == location)
return;
if (minlvl < level)
minlvl = level;
var met_list = GameInfo.getLocationList((GameVersion)pkm.Version, SAV.Generation, egg: false);
var locstr = met_list.FirstOrDefault(loc => loc.Value == location)?.Text;
string suggestion = $"Suggested:\nMet Location: {locstr}\nMet Level: {level}";
var suggestion = new List<string> {"Suggested:"};
if (pkm.Format >= 3)
{
var met_list = GameInfo.getLocationList((GameVersion)pkm.Version, SAV.Generation, egg: false);
var locstr = met_list.FirstOrDefault(loc => loc.Value == location)?.Text;
suggestion.Add($"Met Location: {locstr}");
suggestion.Add($"Met Level: {level}");
}
if (pkm.CurrentLevel < minlvl)
suggestion += $"\nCurrent Level {minlvl}";
suggestion.Add($"Current Level: {minlvl}");
if (WinFormsUtil.Prompt(MessageBoxButtons.YesNo, suggestion) != DialogResult.Yes)
if (suggestion.Count == 1) // no suggestion
return;
TB_MetLevel.Text = level.ToString();
CB_MetLocation.SelectedValue = location;
string suggest = string.Join(Environment.NewLine, suggestion);
if (WinFormsUtil.Prompt(MessageBoxButtons.YesNo, suggest) != DialogResult.Yes)
return;
if (pkm.Format >= 3)
{
TB_MetLevel.Text = level.ToString();
CB_MetLocation.SelectedValue = location;
}
if (pkm.CurrentLevel < minlvl)
TB_Level.Text = minlvl.ToString();
@ -2290,7 +2292,10 @@ namespace PKHeX.WinForms
}
}
else if (PKX.getGender(CB_Form.Text) < 2)
Label_Gender.Text = CB_Form.Text;
{
if (CB_Form.Items.Count == 2) // actually M/F; Pumpkaboo formes in German are S,M,L,XL
Label_Gender.Text = gendersymbols[PKX.getGender(CB_Form.Text)];
}
if (changingFields)
return;
@ -2562,7 +2567,7 @@ namespace PKHeX.WinForms
species = 0; // get the egg name.
// If name is that of another language, don't replace the nickname
if (species != 0 && !PKX.getIsNicknamedAnyLanguage(species, TB_Nickname.Text, SAV.Generation))
if (sender != CB_Language && species != 0 && !PKX.getIsNicknamedAnyLanguage(species, TB_Nickname.Text, SAV.Generation))
return;
TB_Nickname.Text = PKX.getSpeciesNameGeneration(species, lang, SAV.Generation);
@ -2867,7 +2872,9 @@ namespace PKHeX.WinForms
LegalityAnalysis la = new LegalityAnalysis(pk);
if (!la.Parsed)
{
WinFormsUtil.Alert($"Checking legality of PK{pk.Format} files that originated from Gen{pk.GenNumber} is not supported.");
WinFormsUtil.Alert(pk.Format < 3
? $"Checking legality of PK{pk.Format} files is not supported."
: $"Checking legality of PK{pk.Format} files that originated from Gen{pk.GenNumber} is not supported.");
return;
}
if (tabs)
@ -2876,53 +2883,48 @@ namespace PKHeX.WinForms
}
private void updateLegality(LegalityAnalysis la = null, bool skipMoveRepop = false)
{
if (pkm.GenNumber >= 6)
if (!fieldsLoaded)
return;
Legality = la ?? new LegalityAnalysis(pkm);
if (!Legality.Parsed || HaX)
{
if (!fieldsLoaded)
return;
Legality = la ?? new LegalityAnalysis(pkm);
if (!Legality.Parsed || HaX)
{
PB_Legal.Visible = false;
return;
}
PB_Legal.Visible = true;
PB_Legal.Image = Legality.Valid ? Resources.valid : Resources.warn;
// Refresh Move Legality
for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vMoves[i].Valid && !HaX;
for (int i = 0; i < 4; i++)
relearnPB[i].Visible = !Legality.vRelearn[i].Valid && !HaX;
if (skipMoveRepop)
return;
// Resort moves
bool tmp = fieldsLoaded;
fieldsLoaded = false;
var cb = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
var moves = Legality.AllSuggestedMovesAndRelearn;
var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList();
foreach (ComboBox c in cb)
{
var index = WinFormsUtil.getIndex(c);
c.DataSource = new BindingSource(moveList, null);
c.SelectedValue = index;
}
fieldsLoaded |= tmp;
}
else
{
PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible =
PB_Legal.Visible =
PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible =
PB_WarnRelearn1.Visible = PB_WarnRelearn2.Visible = PB_WarnRelearn3.Visible = PB_WarnRelearn4.Visible = false;
return;
}
PB_Legal.Visible = true;
PB_Legal.Image = Legality.Valid ? Resources.valid : Resources.warn;
// Refresh Move Legality
for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vMoves[i].Valid && !HaX;
for (int i = 0; i < 4; i++)
relearnPB[i].Visible = !Legality.vRelearn[i].Valid && !HaX && pkm.Format >= 6;
if (skipMoveRepop)
return;
// Resort moves
bool tmp = fieldsLoaded;
fieldsLoaded = false;
var cb = new[] {CB_Move1, CB_Move2, CB_Move3, CB_Move4};
var moves = Legality.AllSuggestedMovesAndRelearn;
var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList();
foreach (ComboBox c in cb)
{
var index = WinFormsUtil.getIndex(c);
c.DataSource = new BindingSource(moveList, null);
c.SelectedValue = index;
}
fieldsLoaded |= tmp;
}
private void updateGender()
{
int cg = Array.IndexOf(gendersymbols, Label_Gender.Text);
int cg = PKX.getGender(Label_Gender.Text);
int gt = SAV.Personal.getFormeEntry(WinFormsUtil.getIndex(CB_Species), CB_Form.SelectedIndex).Gender;
int Gender;
@ -3848,6 +3850,7 @@ namespace PKHeX.WinForms
bool? noSetb = getPKMSetOverride();
SAV.loadBoxes(path, out result, CB_BoxSelect.SelectedIndex, clearAll, noSetb);
setPKXBoxes();
WinFormsUtil.Alert(result);
}
private void B_SaveBoxBin_Click(object sender, EventArgs e)
@ -4121,7 +4124,12 @@ namespace PKHeX.WinForms
pb.BackgroundImage = null;
if (DragInfo.SameBox && DragInfo.DestinationValid)
SlotPictureBoxes[DragInfo.slotDestinationSlotNumber].Image = img;
{
if (SAV.getIsTeamSet(box, DragInfo.slotDestinationSlotNumber) ^ SAV.getIsTeamSet(box, DragInfo.slotSourceSlotNumber))
getQuickFiller(SlotPictureBoxes[DragInfo.slotDestinationSlotNumber], SAV.getStoredSlot(DragInfo.slotDestinationOffset));
else
SlotPictureBoxes[DragInfo.slotDestinationSlotNumber].Image = img;
}
if (result == DragDropEffects.Copy) // viewed in tabs, apply 'view' highlight
getSlotColor(DragInfo.slotSourceSlotNumber, Resources.slotView);

View file

@ -29,16 +29,33 @@ namespace PKHeX.WinForms
try
{
if (GetFrameworkVersion() >= 393295)
if (IsOnWindows())
{
StartPKHeX();
if (GetFrameworkVersion() >= 393295)
{
StartPKHeX();
}
else
{
// Todo: make this translatable
MessageBox.Show(".NET Framework 4.6 needs to be installed for this version of PKHeX to run.", "PKHeX Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
Process.Start(@"https://www.microsoft.com/download/details.aspx?id=48130");
}
}
else
{
// Todo: make this translatable
MessageBox.Show(".NET Framework 4.6 needs to be installed for this version of PKHeX to run.", "PKHeX Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
Process.Start(@"https://www.microsoft.com/download/details.aspx?id=48130");
//CLR Version 4.0.30319.42000 is equivalent to .NET Framework version 4.6
if ((Environment.Version.CompareTo(Version.Parse("4.0.30319.42000"))) >= 0)
{
StartPKHeX();
}
else
{
MessageBox.Show("Your version of Mono needs to target the .NET Framework 4.6 or higher for this version of PKHeX to run.",
"PKHeX Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
}
catch (FileNotFoundException ex)
{
@ -49,11 +66,9 @@ namespace PKHeX.WinForms
MessageBox.Show("Could not locate PKHeX.Core.dll. Make sure you're running PKHeX together with its code library. Usually caused when all files are not extracted.", "PKHeX Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
return;
}
else
{
// Exception came from Main
throw;
}
// Exception came from Main
throw;
}
}
@ -65,6 +80,16 @@ namespace PKHeX.WinForms
Application.Run(new Main());
}
public static bool IsOnWindows()
{
// 4 -> UNIX, 6 -> Mac OSX, 128 -> UNIX (old)
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 6) || (p == 128))
return false;
return true;
}
public static int GetFrameworkVersion()
{
const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";

View file

@ -32,17 +32,17 @@ namespace PKHeX.WinForms
{
var p = new string[types.Length][];
for (int i = 0; i < p.Length; i++)
p[i] = ReflectUtil.getPropertiesCanWritePublic(types[i]).ToArray();
p[i] = ReflectUtil.getPropertiesCanWritePublic(types[i]).Concat(CustomProperties).OrderBy(a => a).ToArray();
IEnumerable<string> all = p.SelectMany(prop => prop).Distinct();
IEnumerable<string> any = p[0];
for (int i = 1; i < p.Length; i++)
any = any.Union(p[i]);
// Properties for any PKM
var any = p.SelectMany(prop => prop).Distinct().ToArray();
// Properties shared by all PKM
var all = p.Skip(1).Aggregate(new HashSet<string>(p.First()), (h, e) => { h.IntersectWith(e); return h; }).ToArray();
var p1 = new string[types.Length + 2][];
Array.Copy(p, 0, p1, 1, p.Length);
p1[0] = all.ToArray();
p1[p1.Length-1] = any.ToArray();
p1[0] = all;
p1[p1.Length - 1] = any;
return p1;
}
@ -50,6 +50,11 @@ namespace PKHeX.WinForms
private readonly PKM pkmref;
private const string CONST_RAND = "$rand";
private const string CONST_SHINY = "$shiny";
private const string CONST_SUGGEST = "$suggest";
private const string PROP_LEGAL = "Legal";
private static readonly string[] CustomProperties = {PROP_LEGAL};
private int currentFormat = -1;
private static readonly Type[] types = {typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof (PK3)};
private static readonly string[][] properties = getPropArray();
@ -80,15 +85,61 @@ namespace PKHeX.WinForms
runBackgroundWorker();
}
private void B_Add_Click(object sender, EventArgs e)
{
if (CB_Property.SelectedIndex < 0)
{ WinFormsUtil.Alert("Invalid property selected."); return; }
char[] prefix = { '.', '=', '!' };
string s = prefix[CB_Require.SelectedIndex] + CB_Property.Items[CB_Property.SelectedIndex].ToString() + "=";
if (RTB_Instructions.Lines.Length != 0 && RTB_Instructions.Lines.Last().Length > 0)
s = Environment.NewLine + s;
RTB_Instructions.AppendText(s);
}
private void CB_Format_SelectedIndexChanged(object sender, EventArgs e)
{
if (currentFormat == CB_Format.SelectedIndex)
return;
int format = CB_Format.SelectedIndex;
CB_Property.Items.Clear();
CB_Property.Items.AddRange(properties[format]);
CB_Property.SelectedIndex = 0;
currentFormat = format;
}
private void CB_Property_SelectedIndexChanged(object sender, EventArgs e)
{
L_PropType.Text = getPropertyType(CB_Property.Text);
L_PropValue.Text = pkmref.GetType().HasProperty(CB_Property.Text)
? ReflectUtil.GetValue(pkmref, CB_Property.Text).ToString()
: "";
}
private void tabMain_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
private void tabMain_DragDrop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (!Directory.Exists(files[0]))
return;
TB_Folder.Text = files[0];
TB_Folder.Visible = true;
RB_SAV.Checked = false;
RB_Path.Checked = true;
}
private BackgroundWorker b = new BackgroundWorker { WorkerReportsProgress = true };
private void runBackgroundWorker()
{
var Filters = getFilters().ToList();
var Filters = StringInstruction.getFilters(RTB_Instructions.Lines).ToArray();
if (Filters.Any(z => string.IsNullOrWhiteSpace(z.PropertyValue)))
{ WinFormsUtil.Error("Empty Filter Value detected."); return; }
var Instructions = getInstructions().ToList();
var Instructions = StringInstruction.getInstructions(RTB_Instructions.Lines).ToArray();
var emptyVal = Instructions.Where(z => string.IsNullOrWhiteSpace(z.PropertyValue)).ToArray();
if (emptyVal.Any())
{
@ -99,6 +150,9 @@ namespace PKHeX.WinForms
return;
}
if (!Instructions.Any())
{ WinFormsUtil.Error("No instructions defined."); return; }
string destPath = "";
if (RB_Path.Checked)
{
@ -118,11 +172,14 @@ namespace PKHeX.WinForms
screenStrings(Instructions);
b.DoWork += (sender, e) => {
len = err = ctr = 0;
if (RB_SAV.Checked)
{
var data = Main.SAV.BoxData;
setupProgressBar(data.Length);
processSAV(data, Filters, Instructions);
Main.SAV.BoxData = data;
}
else
{
@ -164,100 +221,135 @@ namespace PKHeX.WinForms
// Mass Editing
private int ctr, len, err;
private IEnumerable<StringInstruction> getFilters()
private void processSAV(PKM[] data, StringInstruction[] Filters, StringInstruction[] Instructions)
{
var raw =
RTB_Instructions.Lines
.Where(line => !string.IsNullOrWhiteSpace(line))
.Where(line => new[] {'!','='}.Contains(line[0]));
return from line in raw
let eval = line[0] == '='
let split = line.Substring(1).Split('=')
where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0])
select new StringInstruction {PropertyName = split[0], PropertyValue = split[1], Evaluator = eval};
}
private IEnumerable<StringInstruction> getInstructions()
{
var raw =
RTB_Instructions.Lines
.Where(line => !string.IsNullOrEmpty(line))
.Where(line => new[] {'.'}.Contains(line[0]))
.Select(line => line.Substring(1));
return from line in raw
select line.Split('=') into split
where split.Length == 2
select new StringInstruction { PropertyName = split[0], PropertyValue = split[1] };
}
private void processSAV(PKM[] data, List<StringInstruction> Filters, List<StringInstruction> Instructions)
{
len = err = ctr = 0;
for (int i = 0; i < data.Length; i++)
{
var pkm = data[i];
if (!pkm.Valid || pkm.Locked)
{
b.ReportProgress(i);
continue;
}
ModifyResult r = ProcessPKM(pkm, Filters, Instructions);
if (r != ModifyResult.Invalid)
len++;
if (r == ModifyResult.Error)
err++;
if (r == ModifyResult.Modified)
{
if (pkm.Species != 0)
pkm.RefreshChecksum();
ctr++;
}
processPKM(data[i], Filters, Instructions);
b.ReportProgress(i);
}
Main.SAV.BoxData = data;
}
private void processFolder(string[] files, List<StringInstruction> Filters, List<StringInstruction> Instructions, string destPath)
private void processFolder(string[] files, StringInstruction[] Filters, StringInstruction[] Instructions, string destPath)
{
len = err = ctr = 0;
for (int i = 0; i < files.Length; i++)
{
string file = files[i];
if (!PKX.getIsPKM(new FileInfo(file).Length))
var fi = new FileInfo(file);
if (!PKX.getIsPKM(fi.Length))
{
b.ReportProgress(i);
continue;
}
int format = fi.Extension.Length > 0 ? (fi.Extension.Last() - 0x30) & 7 : Main.SAV.Generation;
byte[] data = File.ReadAllBytes(file);
var pkm = PKMConverter.getPKMfromBytes(data, prefer: Main.SAV.Generation);
if (!pkm.Valid)
{
b.ReportProgress(i);
continue;
}
ModifyResult r = ProcessPKM(pkm, Filters, Instructions);
if (r != ModifyResult.Invalid)
len++;
if (r == ModifyResult.Error)
err++;
if (r == ModifyResult.Modified)
{
if (pkm.Species > 0)
{
pkm.RefreshChecksum();
File.WriteAllBytes(Path.Combine(destPath, Path.GetFileName(file)), pkm.DecryptedBoxData);
ctr++;
}
}
var pkm = PKMConverter.getPKMfromBytes(data, prefer: format);
if (processPKM(pkm, Filters, Instructions))
File.WriteAllBytes(Path.Combine(destPath, Path.GetFileName(file)), pkm.DecryptedBoxData);
b.ReportProgress(i);
}
}
private bool processPKM(PKM pkm, IEnumerable<StringInstruction> Filters, IEnumerable<StringInstruction> Instructions)
{
if (!pkm.Valid || pkm.Locked)
return false;
ModifyResult r = tryModifyPKM(pkm, Filters, Instructions);
if (r != ModifyResult.Invalid)
len++;
if (r == ModifyResult.Error)
err++;
if (r != ModifyResult.Modified)
return false;
if (pkm.Species <= 0)
return false;
pkm.RefreshChecksum();
ctr++;
return true;
}
private string getPropertyType(string propertyName)
{
if (CustomProperties.Contains(propertyName))
return "Custom";
int typeIndex = CB_Format.SelectedIndex;
if (typeIndex == 0) // All
return types[0].GetProperty(propertyName).PropertyType.Name;
if (typeIndex == properties.Length - 1) // Any
foreach (var p in types.Select(t => t.GetProperty(propertyName)).Where(p => p != null))
return p.PropertyType.Name;
return types[typeIndex - 1].GetProperty(propertyName).PropertyType.Name;
}
// Utility Methods
private enum ModifyResult
{
Invalid,
Error,
Filtered,
Modified,
}
public class StringInstruction
{
public string PropertyName;
public string PropertyValue;
public bool Evaluator;
public void setScreenedValue(string[] arr)
{
int index = Array.IndexOf(arr, PropertyValue);
PropertyValue = index > -1 ? index.ToString() : PropertyValue;
}
// Extra Functionality
public bool Random;
public int Min, Max;
public int RandomValue => Util.rand.Next(Min, Max + 1);
public static IEnumerable<StringInstruction> getFilters(IEnumerable<string> lines)
{
var raw = lines
.Where(line => !string.IsNullOrWhiteSpace(line))
.Where(line => new[] { '!', '=' }.Contains(line[0]));
return from line in raw
let eval = line[0] == '='
let split = line.Substring(1).Split('=')
where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0])
select new StringInstruction { PropertyName = split[0], PropertyValue = split[1], Evaluator = eval };
}
public static IEnumerable<StringInstruction> getInstructions(IEnumerable<string> lines)
{
var raw = lines
.Where(line => !string.IsNullOrEmpty(line))
.Where(line => new[] { '.' }.Contains(line[0]))
.Select(line => line.Substring(1));
return from line in raw
select line.Split('=') into split
where split.Length == 2
select new StringInstruction { PropertyName = split[0], PropertyValue = split[1] };
}
}
private class PKMInfo
{
private readonly PKM pkm;
public PKMInfo(PKM pk) { pkm = pk; }
private LegalityAnalysis la;
private LegalityAnalysis Legality => la ?? (la = new LegalityAnalysis(pkm));
public bool Legal => Legality.Valid;
public int[] SuggestedRelearn => Legality.getSuggestedRelearn();
public int[] SuggestedMoves => Legality.getSuggestedMoves(tm: true, tutor: true, reminder: false);
public EncounterStatic SuggestedEncounter => Legality.getSuggestedMetInfo();
}
public static void screenStrings(IEnumerable<StringInstruction> il)
{
foreach (var i in il.Where(i => !i.PropertyValue.All(char.IsDigit)))
@ -298,77 +390,27 @@ namespace PKHeX.WinForms
}
}
}
private void tabMain_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy;
}
private void tabMain_DragDrop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (!Directory.Exists(files[0])) return;
TB_Folder.Text = files[0];
TB_Folder.Visible = true;
RB_SAV.Checked = false;
RB_Path.Checked = true;
}
private void CB_Property_SelectedIndexChanged(object sender, EventArgs e)
{
L_PropType.Text = getPropertyType(CB_Property.Text);
L_PropValue.Text = pkmref.GetType().HasProperty(CB_Property.Text)
? ReflectUtil.GetValue(pkmref, CB_Property.Text).ToString()
: "";
}
private string getPropertyType(string propertyName)
{
int typeIndex = CB_Format.SelectedIndex;
if (typeIndex == 0) // All
return types[0].GetProperty(propertyName).PropertyType.Name;
if (typeIndex == properties.Length - 1) // Any
foreach (var p in types.Select(t => t.GetProperty(propertyName)).Where(p => p != null))
return p.PropertyType.Name;
return types[typeIndex - 1].GetProperty(propertyName).PropertyType.Name;
}
// Utility Methods
public class StringInstruction
{
public string PropertyName;
public string PropertyValue;
public bool Evaluator;
public void setScreenedValue(string[] arr)
{
int index = Array.IndexOf(arr, PropertyValue);
PropertyValue = index > -1 ? index.ToString() : PropertyValue;
}
// Extra Functionality
public bool Random;
public int Min, Max;
public int RandomValue => Util.rand.Next(Min, Max + 1);
}
private enum ModifyResult
{
Invalid,
Error,
Filtered,
Modified,
}
private static ModifyResult ProcessPKM(PKM PKM, IEnumerable<StringInstruction> Filters, IEnumerable<StringInstruction> Instructions)
private static ModifyResult tryModifyPKM(PKM PKM, IEnumerable<StringInstruction> Filters, IEnumerable<StringInstruction> Instructions)
{
if (!PKM.ChecksumValid || PKM.Species == 0)
return ModifyResult.Invalid;
Type pkm = PKM.GetType();
PKMInfo info = new PKMInfo(PKM);
foreach (var cmd in Filters)
{
try
{
if (cmd.PropertyName == PROP_LEGAL)
{
bool legal;
if (!bool.TryParse(cmd.PropertyValue, out legal))
return ModifyResult.Error;
if (legal == info.Legal == cmd.Evaluator)
continue;
return ModifyResult.Filtered;
}
if (!pkm.HasProperty(cmd.PropertyName))
return ModifyResult.Filtered;
if (ReflectUtil.GetValueEquals(PKM, cmd.PropertyName, cmd.PropertyValue) != cmd.Evaluator)
@ -386,76 +428,100 @@ namespace PKHeX.WinForms
{
try
{
if (cmd.PropertyName == nameof(PKM.MetDate))
PKM.MetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
else if (cmd.PropertyName == nameof(PKM.EggMetDate))
PKM.EggMetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == CONST_RAND)
ReflectUtil.SetValue(PKM, cmd.PropertyName, Util.rnd32().ToString());
else if ((cmd.PropertyName == nameof(PKM.Ability) || cmd.PropertyName == nameof(PKM.AbilityNumber)) && cmd.PropertyValue.StartsWith("$"))
PKM.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30);
else if(cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_RAND)
PKM.setPIDGender(PKM.Gender);
else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == nameof(PKM.PID))
PKM.EncryptionConstant = PKM.PID;
else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_SHINY)
PKM.setShinyPID();
else if (cmd.PropertyName == nameof(PKM.Species) && cmd.PropertyValue == "0")
PKM.Data = new byte[PKM.Data.Length];
else if (cmd.PropertyName.StartsWith("IV") && cmd.PropertyValue == CONST_RAND)
setRandomIVs(PKM, cmd);
else if (cmd.Random)
ReflectUtil.SetValue(PKM, cmd.PropertyName, cmd.RandomValue);
if (cmd.PropertyValue == CONST_SUGGEST)
{
result = setSuggestedProperty(PKM, cmd, info)
? ModifyResult.Modified
: ModifyResult.Error;
}
else
ReflectUtil.SetValue(PKM, cmd.PropertyName, cmd.PropertyValue);
result = ModifyResult.Modified;
{
setProperty(PKM, cmd);
result = ModifyResult.Modified;
}
}
catch { Console.WriteLine($"Unable to set {cmd.PropertyName} to {cmd.PropertyValue}."); }
}
return result;
}
private static bool setSuggestedProperty(PKM PKM, StringInstruction cmd, PKMInfo info)
{
switch (cmd.PropertyName)
{
case nameof(PKM.RelearnMoves):
PKM.RelearnMoves = info.SuggestedRelearn;
return true;
case nameof(PKM.Met_Location):
var encounter = info.SuggestedEncounter;
if (encounter == null)
return false;
int level = encounter.Level;
int location = encounter.Location;
int minlvl = Legal.getLowestLevel(PKM, encounter.Species);
PKM.Met_Level = level;
PKM.Met_Location = location;
PKM.CurrentLevel = Math.Max(minlvl, level);
return true;
case nameof(PKM.Moves):
var moves = info.SuggestedMoves;
Util.Shuffle(moves);
Array.Resize(ref moves, 4);
PKM.Moves = moves;
return true;
default:
return false;
}
}
private static void setProperty(PKM PKM, StringInstruction cmd)
{
if (cmd.PropertyName == nameof(PKM.MetDate))
PKM.MetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
else if (cmd.PropertyName == nameof(PKM.EggMetDate))
PKM.EggMetDate = DateTime.ParseExact(cmd.PropertyValue, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == CONST_RAND)
ReflectUtil.SetValue(PKM, cmd.PropertyName, Util.rnd32().ToString());
else if ((cmd.PropertyName == nameof(PKM.Ability) || cmd.PropertyName == nameof(PKM.AbilityNumber)) && cmd.PropertyValue.StartsWith("$"))
PKM.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30);
else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_RAND)
PKM.setPIDGender(PKM.Gender);
else if (cmd.PropertyName == nameof(PKM.EncryptionConstant) && cmd.PropertyValue == nameof(PKM.PID))
PKM.EncryptionConstant = PKM.PID;
else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_SHINY)
PKM.setShinyPID();
else if (cmd.PropertyName == nameof(PKM.Species) && cmd.PropertyValue == "0")
PKM.Data = new byte[PKM.Data.Length];
else if (cmd.PropertyName.StartsWith("IV") && cmd.PropertyValue == CONST_RAND)
setRandomIVs(PKM, cmd);
else if (cmd.Random)
ReflectUtil.SetValue(PKM, cmd.PropertyName, cmd.RandomValue);
else if (cmd.PropertyName == nameof(PKM.IsNicknamed) && cmd.PropertyValue.ToLower() == "false")
{ PKM.IsNicknamed = false; PKM.Nickname = PKX.getSpeciesName(PKM.Species, PKM.Language); }
else
ReflectUtil.SetValue(PKM, cmd.PropertyName, cmd.PropertyValue);
}
private static void setRandomIVs(PKM PKM, StringInstruction cmd)
{
int MaxIV = PKM.Format <= 2 ? 15 : 31;
if (cmd.PropertyName == "IVs")
{
bool IV3 = Legal.Legends.Contains(PKM.Species) || Legal.SubLegends.Contains(PKM.Species);
int[] IVs = new int[6];
do
{
for (int i = 0; i < 6; i++)
IVs[i] = (int)(Util.rnd32() & MaxIV);
} while (IV3 && IVs.Where(i => i == MaxIV).Count() < 3);
ReflectUtil.SetValue(PKM, cmd.PropertyName, IVs);
for (int i = 0; i < 6; i++)
IVs[i] = (int)(Util.rnd32() & MaxIV);
if (Legal.Legends.Contains(PKM.Species) || Legal.SubLegends.Contains(PKM.Species))
for (int i = 0; i < 3; i++)
IVs[i] = MaxIV;
Util.Shuffle(IVs);
PKM.IVs = IVs;
}
else
ReflectUtil.SetValue(PKM, cmd.PropertyName, Util.rnd32() & MaxIV);
}
private void B_Add_Click(object sender, EventArgs e)
{
if (CB_Property.SelectedIndex < 0)
{ WinFormsUtil.Alert("Invalid property selected."); return; }
char[] prefix = {'.', '=', '!'};
string s = prefix[CB_Require.SelectedIndex] + CB_Property.Items[CB_Property.SelectedIndex].ToString() + "=";
if (RTB_Instructions.Lines.Length != 0 && RTB_Instructions.Lines.Last().Length > 0)
s = Environment.NewLine + s;
RTB_Instructions.AppendText(s);
}
private void CB_Format_SelectedIndexChanged(object sender, EventArgs e)
{
if (currentFormat == CB_Format.SelectedIndex)
return;
int format = CB_Format.SelectedIndex;
CB_Property.Items.Clear();
CB_Property.Items.AddRange(properties[format]);
CB_Property.SelectedIndex = 0;
currentFormat = format;
}
}
}

View file

@ -488,17 +488,7 @@ namespace PKHeX.WinForms
if (RTB_Instructions.Lines.Any(line => line.Length > 0))
{
var raw =
RTB_Instructions.Lines
.Where(line => !string.IsNullOrWhiteSpace(line))
.Where(line => new[] { '!', '=' }.Contains(line[0]));
var filters = (from line in raw
let eval = line[0] == '='
let split = line.Substring(1).Split('=')
where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0])
select new BatchEditor.StringInstruction { PropertyName = split[0], PropertyValue = split[1], Evaluator = eval }).ToArray();
var filters = BatchEditor.StringInstruction.getFilters(RTB_Instructions.Lines).ToArray();
BatchEditor.screenStrings(filters);
res = res.Where(pkm => // Compare across all filters
{

View file

@ -73,7 +73,7 @@ namespace PKHeX.WinForms
CB_Move1.DisplayMember = CB_Move2.DisplayMember = CB_Move3.DisplayMember = CB_Move4.DisplayMember = "Text";
CB_Move1.ValueMember = CB_Move2.ValueMember = CB_Move3.ValueMember = CB_Move4.ValueMember = "Value";
var MoveList = GameInfo.MoveDataSource.Where(m => m.Value <= SAV.MaxMoveID).ToList();
var MoveList = GameInfo.MoveDataSource;
CB_Move1.DataSource = new BindingSource(MoveList, null);
CB_Move2.DataSource = new BindingSource(MoveList, null);
CB_Move3.DataSource = new BindingSource(MoveList, null);

View file

@ -45,7 +45,7 @@ namespace PKHeX.WinForms
CB_Move1.DisplayMember = CB_Move2.DisplayMember = CB_Move3.DisplayMember = CB_Move4.DisplayMember = "Text";
CB_Move1.ValueMember = CB_Move2.ValueMember = CB_Move3.ValueMember = CB_Move4.ValueMember = "Value";
var MoveList = GameInfo.MoveDataSource.Where(m => m.Value <= SAV.MaxMoveID).ToList();
var MoveList = GameInfo.MoveDataSource;
CB_Move1.DataSource = new BindingSource(MoveList, null);
CB_Move2.DataSource = new BindingSource(MoveList, null);
CB_Move3.DataSource = new BindingSource(MoveList, null);

View file

@ -39,6 +39,9 @@ namespace PKHeX.WinForms
pba = new [] { PB_Badge1, PB_Badge2, PB_Badge3, PB_Badge4, PB_Badge5, PB_Badge6, PB_Badge7, PB_Badge8, };
L_MultiplayerSprite.Enabled = CB_MultiplayerSprite.Enabled = Main.SAV.ORAS;
L_MultiplayerSprite.Visible = CB_MultiplayerSprite.Visible = Main.SAV.ORAS;
PB_Sprite.Visible = Main.SAV.ORAS;
L_Style.Visible = TB_Style.Visible = SAV.XY;
if (!SAV.XY)
TC_Editor.TabPages.Remove(Tab_Appearance);

View file

@ -104,6 +104,9 @@ namespace PKHeX.WinForms
this.L_SinglesC = new System.Windows.Forms.Label();
this.TC_Editor = new System.Windows.Forms.TabControl();
this.Tab_Overview = new System.Windows.Forms.TabPage();
this.B_GenTID = new System.Windows.Forms.Button();
this.L_G7TID = new System.Windows.Forms.Label();
this.MT_G7TID = new System.Windows.Forms.MaskedTextBox();
this.GB_Stats = new System.Windows.Forms.GroupBox();
this.NUD_Stat = new System.Windows.Forms.NumericUpDown();
this.NUD_FC = new System.Windows.Forms.NumericUpDown();
@ -171,9 +174,8 @@ namespace PKHeX.WinForms
this.NUD_ThumbsTotal = new System.Windows.Forms.NumericUpDown();
this.L_ThumbsTotal = new System.Windows.Forms.Label();
this.B_Fashion = new System.Windows.Forms.Button();
this.L_G7TID = new System.Windows.Forms.Label();
this.MT_G7TID = new System.Windows.Forms.MaskedTextBox();
this.B_GenTID = new System.Windows.Forms.Button();
this.TB_PlazaName = new System.Windows.Forms.TextBox();
this.L_PlazaName = new System.Windows.Forms.Label();
this.TC_Editor.SuspendLayout();
this.Tab_Overview.SuspendLayout();
this.GB_Stats.SuspendLayout();
@ -847,6 +849,35 @@ namespace PKHeX.WinForms
this.Tab_Overview.Text = "Overview";
this.Tab_Overview.UseVisualStyleBackColor = true;
//
// B_GenTID
//
this.B_GenTID.Location = new System.Drawing.Point(124, 75);
this.B_GenTID.Name = "B_GenTID";
this.B_GenTID.Size = new System.Drawing.Size(68, 20);
this.B_GenTID.TabIndex = 63;
this.B_GenTID.Text = "Generate";
this.B_GenTID.UseVisualStyleBackColor = true;
this.B_GenTID.Click += new System.EventHandler(this.B_GenTID_Click);
//
// L_G7TID
//
this.L_G7TID.Location = new System.Drawing.Point(-1, 78);
this.L_G7TID.Name = "L_G7TID";
this.L_G7TID.Size = new System.Drawing.Size(48, 13);
this.L_G7TID.TabIndex = 61;
this.L_G7TID.Text = "G7TID:";
this.L_G7TID.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// MT_G7TID
//
this.MT_G7TID.Location = new System.Drawing.Point(53, 75);
this.MT_G7TID.Mask = "000000";
this.MT_G7TID.Name = "MT_G7TID";
this.MT_G7TID.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.MT_G7TID.Size = new System.Drawing.Size(65, 20);
this.MT_G7TID.TabIndex = 62;
this.MT_G7TID.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// GB_Stats
//
this.GB_Stats.Controls.Add(this.NUD_Stat);
@ -1081,7 +1112,7 @@ namespace PKHeX.WinForms
this.Tab_BadgeMap.Controls.Add(this.GB_Map);
this.Tab_BadgeMap.Location = new System.Drawing.Point(4, 22);
this.Tab_BadgeMap.Name = "Tab_BadgeMap";
this.Tab_BadgeMap.Size = new System.Drawing.Size(386, 294);
this.Tab_BadgeMap.Size = new System.Drawing.Size(386, 290);
this.Tab_BadgeMap.TabIndex = 3;
this.Tab_BadgeMap.Text = "Map";
this.Tab_BadgeMap.UseVisualStyleBackColor = true;
@ -1272,7 +1303,7 @@ namespace PKHeX.WinForms
this.Tab_BattleTree.Location = new System.Drawing.Point(4, 22);
this.Tab_BattleTree.Name = "Tab_BattleTree";
this.Tab_BattleTree.Padding = new System.Windows.Forms.Padding(3);
this.Tab_BattleTree.Size = new System.Drawing.Size(386, 294);
this.Tab_BattleTree.Size = new System.Drawing.Size(386, 290);
this.Tab_BattleTree.TabIndex = 5;
this.Tab_BattleTree.Text = "Battle Tree";
this.Tab_BattleTree.UseVisualStyleBackColor = true;
@ -1555,6 +1586,8 @@ namespace PKHeX.WinForms
//
// Tab_Misc
//
this.Tab_Misc.Controls.Add(this.L_PlazaName);
this.Tab_Misc.Controls.Add(this.TB_PlazaName);
this.Tab_Misc.Controls.Add(this.CB_Fashion);
this.Tab_Misc.Controls.Add(this.L_SkinColor);
this.Tab_Misc.Controls.Add(this.CB_SkinColor);
@ -1563,7 +1596,7 @@ namespace PKHeX.WinForms
this.Tab_Misc.Location = new System.Drawing.Point(4, 22);
this.Tab_Misc.Name = "Tab_Misc";
this.Tab_Misc.Padding = new System.Windows.Forms.Padding(3);
this.Tab_Misc.Size = new System.Drawing.Size(386, 294);
this.Tab_Misc.Size = new System.Drawing.Size(386, 290);
this.Tab_Misc.TabIndex = 4;
this.Tab_Misc.Text = "Misc";
this.Tab_Misc.UseVisualStyleBackColor = true;
@ -1738,34 +1771,25 @@ namespace PKHeX.WinForms
this.B_Fashion.UseVisualStyleBackColor = true;
this.B_Fashion.Click += new System.EventHandler(this.B_Fashion_Click);
//
// L_G7TID
// TB_PlazaName
//
this.L_G7TID.Location = new System.Drawing.Point(-1, 78);
this.L_G7TID.Name = "L_G7TID";
this.L_G7TID.Size = new System.Drawing.Size(48, 13);
this.L_G7TID.TabIndex = 61;
this.L_G7TID.Text = "G7TID:";
this.L_G7TID.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.TB_PlazaName.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.TB_PlazaName.Location = new System.Drawing.Point(217, 139);
this.TB_PlazaName.MaxLength = 20;
this.TB_PlazaName.Name = "TB_PlazaName";
this.TB_PlazaName.Size = new System.Drawing.Size(159, 20);
this.TB_PlazaName.TabIndex = 61;
this.TB_PlazaName.Text = "WWWWWWWWWWWWWWWWWWWW";
//
// MT_G7TID
// L_PlazaName
//
this.MT_G7TID.Location = new System.Drawing.Point(53, 75);
this.MT_G7TID.Mask = "000000";
this.MT_G7TID.Name = "MT_G7TID";
this.MT_G7TID.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.MT_G7TID.Size = new System.Drawing.Size(65, 20);
this.MT_G7TID.TabIndex = 62;
this.MT_G7TID.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// B_GenTID
//
this.B_GenTID.Location = new System.Drawing.Point(124, 75);
this.B_GenTID.Name = "B_GenTID";
this.B_GenTID.Size = new System.Drawing.Size(68, 20);
this.B_GenTID.TabIndex = 63;
this.B_GenTID.Text = "Generate";
this.B_GenTID.UseVisualStyleBackColor = true;
this.B_GenTID.Click += new System.EventHandler(this.B_GenTID_Click);
this.L_PlazaName.AutoSize = true;
this.L_PlazaName.Location = new System.Drawing.Point(214, 119);
this.L_PlazaName.Name = "L_PlazaName";
this.L_PlazaName.Size = new System.Drawing.Size(106, 13);
this.L_PlazaName.TabIndex = 62;
this.L_PlazaName.Text = "Festival Plaza Name:";
this.L_PlazaName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// SAV_Trainer7
//
@ -1813,6 +1837,7 @@ namespace PKHeX.WinForms
((System.ComponentModel.ISupportInitialize)(this.NUD_RMStreak0)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.NUD_RCStreak0)).EndInit();
this.Tab_Misc.ResumeLayout(false);
this.Tab_Misc.PerformLayout();
this.GB_PokeFinder.ResumeLayout(false);
this.GB_PokeFinder.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.NUD_SnapCount)).EndInit();
@ -1969,5 +1994,7 @@ namespace PKHeX.WinForms
private System.Windows.Forms.Button B_GenTID;
private System.Windows.Forms.Label L_G7TID;
private System.Windows.Forms.MaskedTextBox MT_G7TID;
private System.Windows.Forms.TextBox TB_PlazaName;
private System.Windows.Forms.Label L_PlazaName;
}
}

View file

@ -182,6 +182,7 @@ namespace PKHeX.WinForms
NUD_SMStreak2.Value = Math.Min(NUD_SMStreak2.Maximum, SAV.getTreeStreak(2, super: true, max: true));
CB_SkinColor.SelectedIndex = SAV.DressUpSkinColor;
TB_PlazaName.Text = SAV.FestivalPlazaName;
}
private void save()
{
@ -262,6 +263,8 @@ namespace PKHeX.WinForms
if (SAV.DressUpSkinColor != CB_SkinColor.SelectedIndex &&
(SAV.Gender == skin || DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, $"Gender-Skin mismatch:\nGender: {gStr}, Skin: {sStr}", "Save selected Skin Color?")))
SAV.DressUpSkinColor = CB_SkinColor.SelectedIndex;
SAV.FestivalPlazaName = TB_PlazaName.Text;
}
private void clickOT(object sender, MouseEventArgs e)

View file

@ -203,7 +203,12 @@ namespace PKHeX.WinForms
pb.BackgroundImage = null;
if (DragInfo.SameBox && DragInfo.DestinationValid)
SlotPictureBoxes[DragInfo.slotDestinationSlotNumber].Image = img;
{
if (SAV.getIsTeamSet(box, DragInfo.slotDestinationSlotNumber) ^ SAV.getIsTeamSet(box, DragInfo.slotSourceSlotNumber))
getQuickFiller(SlotPictureBoxes[DragInfo.slotDestinationSlotNumber], SAV.getStoredSlot(DragInfo.slotDestinationOffset));
else
SlotPictureBoxes[DragInfo.slotDestinationSlotNumber].Image = img;
}
}
catch (Exception x)
{

View file

@ -104,6 +104,7 @@ namespace PKHeX.WinForms
dgvIndex.DisplayIndex = c++;
dgvIndex.Width = 45;
dgvIndex.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
((DataGridViewTextBoxColumn) dgvIndex).MaxInputLength = (int)(Math.Log10(Math.Max(1, pouch.MaxCount)) + 1);
}
dgv.Columns.Add(dgvItemVal);

View file

@ -492,7 +492,17 @@ namespace PKHeX.WinForms
string s = gift.getCardHeader() + Environment.NewLine;
if (gift.IsItem)
{
s += "Item: " + GameInfo.Strings.itemlist[gift.Item] + Environment.NewLine + "Quantity: " + gift.Quantity + Environment.NewLine;
s += $"Item: {GameInfo.Strings.itemlist[gift.Item]} (Quantity: {gift.Quantity})" + Environment.NewLine;
if (gift is WC7)
{
var ind = 1;
var wc7 = (WC7) gift;
while (wc7.GetItem(ind) != 0)
{
s += $"Item: {GameInfo.Strings.itemlist[wc7.GetItem(ind)]} (Quantity: {wc7.GetQuantity(ind)})" + Environment.NewLine;
ind++;
}
}
}
else if (gift.IsPokémon)
{

View file

@ -20,23 +20,23 @@ namespace PKHeX.WinForms
public string Position => pkm.Identifier;
public Image Sprite => pkm.Sprite();
public string Nickname => pkm.Nickname;
public string Species => GameInfo.Strings.specieslist[pkm.Species];
public string Nature => GameInfo.Strings.natures[pkm.Nature];
public string Gender => Main.gendersymbols[pkm.Gender];
public string Species => get(GameInfo.Strings.specieslist, pkm.Species);
public string Nature => get(GameInfo.Strings.natures, pkm.Nature);
public string Gender => get(Main.gendersymbols, pkm.Gender);
public string ESV => pkm.PSV.ToString("0000");
public string HP_Type => GameInfo.Strings.types[pkm.HPType+1];
public string Ability => GameInfo.Strings.abilitylist[pkm.Ability];
public string Move1 => GameInfo.Strings.movelist[pkm.Move1];
public string Move2 => GameInfo.Strings.movelist[pkm.Move2];
public string Move3 => GameInfo.Strings.movelist[pkm.Move3];
public string Move4 => GameInfo.Strings.movelist[pkm.Move4];
public string HeldItem => GameInfo.Strings.itemlist[pkm.HeldItem];
public string HP_Type => get(GameInfo.Strings.types, pkm.HPType+1);
public string Ability => get(GameInfo.Strings.abilitylist, pkm.Ability);
public string Move1 => get(GameInfo.Strings.movelist, pkm.Move1);
public string Move2 => get(GameInfo.Strings.movelist, pkm.Move2);
public string Move3 => get(GameInfo.Strings.movelist, pkm.Move3);
public string Move4 => get(GameInfo.Strings.movelist, pkm.Move4);
public string HeldItem => get(GameInfo.Strings.itemlist, pkm.HeldItem);
public string MetLoc => pkm.getLocation(eggmet: false);
public string EggLoc => pkm.getLocation(eggmet: true);
public string Ball => GameInfo.Strings.balllist[pkm.Ball];
public string Ball => get(GameInfo.Strings.balllist, pkm.Ball);
public string OT => pkm.OT_Name;
public string Version => GameInfo.Strings.gamelist[pkm.Version];
public string OTLang => GameInfo.Strings.gamelanguages[pkm.Language] ?? $"UNK {pkm.Language}";
public string Version => get(GameInfo.Strings.gamelist, pkm.Version);
public string OTLang => get(GameInfo.Strings.gamelanguages, pkm.Language) ?? $"UNK {pkm.Language}";
public string CountryID => pkm.Format > 5 ? pkm.Country.ToString() : "N/A";
public string RegionID => pkm.Format > 5 ? pkm.Region.ToString() : "N/A";
public string DSRegionID => pkm.Format > 5 ? pkm.ConsoleRegion.ToString() : "N/A";
@ -92,10 +92,10 @@ namespace PKHeX.WinForms
public int Move2_PPUp => pkm.Move2_PPUps;
public int Move3_PPUp => pkm.Move3_PPUps;
public int Move4_PPUp => pkm.Move4_PPUps;
public string Relearn1 => GameInfo.Strings.movelist[pkm.RelearnMove1];
public string Relearn2 => GameInfo.Strings.movelist[pkm.RelearnMove2];
public string Relearn3 => GameInfo.Strings.movelist[pkm.RelearnMove3];
public string Relearn4 => GameInfo.Strings.movelist[pkm.RelearnMove4];
public string Relearn1 => get(GameInfo.Strings.movelist, pkm.RelearnMove1);
public string Relearn2 => get(GameInfo.Strings.movelist, pkm.RelearnMove2);
public string Relearn3 => get(GameInfo.Strings.movelist, pkm.RelearnMove3);
public string Relearn4 => get(GameInfo.Strings.movelist, pkm.RelearnMove4);
public ushort Checksum => pkm.Checksum;
public int mFriendship => pkm.OT_Friendship;
public int OT_Affection => pkm.OT_Affection;
@ -109,6 +109,8 @@ namespace PKHeX.WinForms
#endregion
public Preview(PKM p) { pkm = p; }
private static string get(IReadOnlyList<string> arr, int val) => arr?.Count > val ? arr[val] : null;
}
public frmReport()
{

View file

@ -267,8 +267,8 @@ namespace PKHeX.Core
public static GameStrings Strings;
// DataSource providing
public static List<ComboItem> MoveDataSource, ItemDataSource, SpeciesDataSource, BallDataSource, NatureDataSource, AbilityDataSource, VersionDataSource;
public static List<ComboItem> HaXMoveDataSource;
public static List<ComboItem> ItemDataSource, SpeciesDataSource, BallDataSource, NatureDataSource, AbilityDataSource, VersionDataSource;
public static List<ComboItem> LegalMoveDataSource, HaXMoveDataSource, MoveDataSource;
private static List<ComboItem> metGen2, metGen3, metGen3CXD, metGen4, metGen5, metGen6, metGen7;
public static void InitializeDataSources(GameStrings s)
@ -284,7 +284,7 @@ namespace PKHeX.Core
VersionDataSource.AddRange(Util.getCBList(s.gamelist, Legal.Games_7go).OrderBy(g => g.Value)); // stuff to end unsorted
HaXMoveDataSource = Util.getCBList(s.movelist, null);
MoveDataSource = HaXMoveDataSource.Where(m => !Legal.Z_Moves.Contains(m.Value)).ToList();
MoveDataSource = LegalMoveDataSource = HaXMoveDataSource.Where(m => !Legal.Z_Moves.Contains(m.Value)).ToList();
#region Met Locations
// Gen 2
{
@ -525,5 +525,56 @@ namespace PKHeX.Core
}
catch { return "Illegal"; }
}
/// <summary>
/// Gets the location names array for a specified generation.
/// </summary>
/// <param name="gen">Generation to get location names for.</param>
/// <param name="bankID">BankID used to choose the text bank.</param>
/// <returns>List of location names.</returns>
public static string[] getLocationNames(int gen, int bankID)
{
switch (gen)
{
case 2: return Strings.metGSC_00000;
case 3: return Strings.metRSEFRLG_00000;
case 4:
switch (bankID)
{
case 0: return Strings.metHGSS_00000;
case 2: return Strings.metHGSS_02000;
default: return null;
}
case 5:
switch (bankID)
{
case 0: return Strings.metBW2_00000;
case 3: return Strings.metBW2_30000;
case 4: return Strings.metBW2_40000;
case 6: return Strings.metBW2_60000;
default: return null;
}
case 6:
switch (bankID)
{
case 0: return Strings.metXY_00000;
case 3: return Strings.metXY_30000;
case 4: return Strings.metXY_40000;
case 6: return Strings.metXY_60000;
default: return null;
}
case 7:
switch (bankID)
{
case 0: return Strings.metSM_00000;
case 3: return Strings.metSM_30000;
case 4: return Strings.metSM_40000;
case 6: return Strings.metSM_60000;
default: return null;
}
default:
return null;
}
}
}
}

View file

@ -22,7 +22,7 @@
/*Gen6*/ X = 24, Y = 25, AS = 26, OR = 27,
/*Gen7*/ SN = 30, MN = 31,
/* GO */ GO = 34,
/* VC */ RD = 35, BU = 36, GN = 37, YW = 38,
/* VC */ RD = 35, GN = 36, BU = 37, YW = 38, // GN = Blue for international release
// Game Groupings (SaveFile type)
RBY = 98,
@ -39,6 +39,7 @@
SM = 109,
// Extra Game Groupings (Generation)
Gen1, Gen2, Gen3, Gen4, Gen5, Gen6, Gen7
Gen1, Gen2, Gen3, Gen4, Gen5, Gen6, Gen7,
SPECIAL, // Stadium
}
}

View file

@ -7,13 +7,13 @@ namespace PKHeX.Core
public partial class LegalityAnalysis
{
private PKM pkm;
private DexLevel[] EvoChain;
private DexLevel[][] EvoChainsAllGens;
private readonly List<CheckResult> Parse = new List<CheckResult>();
private object EncounterMatch;
private object EncounterMatch, EncounterOriginal;
private Type EncounterType;
private bool EncounterIsMysteryGift => EncounterType.IsSubclassOf(typeof (MysteryGift));
private string EncounterName => Legal.getEncounterTypeName(pkm, EncounterMatch);
private string EncounterName => Legal.getEncounterTypeName(pkm, EncounterOriginal ?? EncounterMatch);
private List<MysteryGift> EventGiftMatch;
private CheckResult Encounter, History;
private int[] RelearnBase;
@ -39,11 +39,18 @@ namespace PKHeX.Core
try
{
switch (pk.Format) // prior to storing GameVersion
{
case 1: parsePK1(pk); break;
}
if (!Parse.Any())
switch (pk.GenNumber)
{
case 6: parsePK6(pk); break;
case 1: parsePK7(pk); break;
case 7: parsePK7(pk); break;
default: return;
}
Valid = Parsed = Parse.Any();
@ -59,10 +66,12 @@ namespace PKHeX.Core
if (pkm.FatefulEncounter && vRelearn.Any(chk => !chk.Valid) && EncounterMatch == null)
AddLine(Severity.Indeterminate, "Fateful Encounter with no matching Encounter. Has the Mystery Gift data been contributed?", CheckIdentifier.Fateful);
}
else
return;
}
catch { Valid = false; }
AllSuggestedMoves = !pkm.IsOriginValid() ? new int[4] : getSuggestedMoves(true, true, true);
AllSuggestedRelearnMoves = !pkm.IsOriginValid() ? new int[4] : Legal.getValidRelearn(pkm, -1).ToArray();
AllSuggestedMoves = !pkm.IsOriginValid ? new int[4] : getSuggestedMoves(true, true, true);
AllSuggestedRelearnMoves = !pkm.IsOriginValid ? new int[4] : Legal.getValidRelearn(pkm, -1).ToArray();
AllSuggestedMovesAndRelearn = AllSuggestedMoves.Concat(AllSuggestedRelearnMoves).ToArray();
}
@ -74,26 +83,42 @@ namespace PKHeX.Core
{
Parse.Add(chk);
}
private void parsePK1(PKM pk)
{
pkm = pk;
if (!pkm.IsOriginValid)
{ AddLine(Severity.Invalid, "Species does not exist in origin game.", CheckIdentifier.None); return; }
updateEncounterChain();
updateMoveLegality();
updateEncounterInfo();
verifyNickname();
verifyDVs();
verifyG1OT();
}
private void parsePK6(PKM pk)
{
pkm = pk;
if (!pkm.IsOriginValid())
if (!pkm.IsOriginValid)
{ AddLine(Severity.Invalid, "Species does not exist in origin game.", CheckIdentifier.None); return; }
updateRelearnLegality();
updateEncounterChain();
updateMoveLegality();
updateEncounterInfo();
updateChecks();
}
private void parsePK7(PKM pk)
{
pkm = pk;
if (!pkm.IsOriginValid())
if (!pkm.IsOriginValid)
{ AddLine(Severity.Invalid, "Species does not exist in origin game.", CheckIdentifier.None); return; }
updateRelearnLegality();
updateEncounterChain();
updateMoveLegality();
updateEncounterInfo();
updateChecks();
}
@ -116,18 +141,20 @@ namespace PKHeX.Core
EncounterMatch = EventGiftMatch.First(); // temporarily set one so that Encounter can be verified
Encounter = verifyEncounter();
EvoChain = Legal.getEvolutionChain(pkm, EncounterMatch);
Parse.Add(Encounter);
EvoChainsAllGens = Legal.getEvolutionChainsAllGens(pkm, EncounterOriginal ?? EncounterMatch);
}
private void updateChecks()
private void updateEncounterInfo()
{
EncounterMatch = EncounterMatch ?? pkm.Species;
EncounterType = EncounterMatch?.GetType();
EncounterType = (EncounterOriginal ?? EncounterMatch)?.GetType();
if (EncounterType == typeof (MysteryGift))
EncounterType = EncounterType.BaseType;
}
private void updateChecks()
{
History = verifyHistory();
AddLine(Encounter);
AddLine(History);
verifyECPID();
@ -160,6 +187,8 @@ namespace PKHeX.Core
for (int i = 0; i < 4; i++)
if (!vMoves[i].Valid)
r += $"{vMoves[i].Judgement} Move {i + 1}: {vMoves[i].Comment}" + Environment.NewLine;
if (pkm.Format >= 6)
for (int i = 0; i < 4; i++)
if (!vRelearn[i].Valid)
r += $"{vRelearn[i].Judgement} Relearn Move {i + 1}: {vRelearn[i].Comment}" + Environment.NewLine;
@ -186,7 +215,9 @@ namespace PKHeX.Core
for (int i = 0; i < 4; i++)
if (vMoves[i].Valid)
r += $"{vMoves[i].Judgement} Move {i + 1}: {vMoves[i].Comment}" + Environment.NewLine;
for (int i = 0; i < 4; i++)
if (pkm.Format >= 6)
for (int i = 0; i < 4; i++)
if (vRelearn[i].Valid)
r += $"{vRelearn[i].Judgement} Relearn Move {i + 1}: {vRelearn[i].Comment}" + Environment.NewLine;
@ -204,7 +235,7 @@ namespace PKHeX.Core
public int[] getSuggestedRelearn()
{
if (RelearnBase == null || pkm.GenNumber < 6 || !pkm.IsOriginValid())
if (RelearnBase == null || pkm.GenNumber < 6 || !pkm.IsOriginValid)
return new int[4];
if (!pkm.WasEgg)
@ -222,9 +253,11 @@ namespace PKHeX.Core
}
public int[] getSuggestedMoves(bool tm, bool tutor, bool reminder)
{
if (pkm == null || pkm.GenNumber < 6 || !pkm.IsOriginValid())
if (pkm == null || !pkm.IsOriginValid)
return null;
return Legal.getValidMoves(pkm, EvoChain, Tutor: tutor, Machine: tm, MoveReminder: reminder).Skip(1).ToArray(); // skip move 0
if (!Parsed)
return new int[4];
return Legal.getValidMoves(pkm, EvoChainsAllGens, Tutor: tutor, Machine: tm, MoveReminder: reminder).Skip(1).ToArray(); // skip move 0
}
public EncounterStatic getSuggestedMetInfo()
@ -232,24 +265,30 @@ namespace PKHeX.Core
if (pkm == null)
return null;
int loc = getSuggestedTransferLocation(pkm);
if (pkm.WasEgg)
return new EncounterStatic
{
Species = Legal.getBaseSpecies(pkm),
Location = getSuggestedEggMetLocation(pkm),
Location = loc != -1 ? loc : getSuggestedEggMetLocation(pkm),
Level = 1,
};
var capture = Legal.getCaptureLocation(pkm);
if (capture != null)
var area = Legal.getCaptureLocation(pkm);
if (area != null)
{
var slots = area.Slots.OrderBy(s => s.LevelMin);
return new EncounterStatic
{
Species = capture.Slots.First().Species,
Location = capture.Location,
Level = capture.Slots.First().LevelMin,
Species = slots.First().Species,
Location = loc != -1 ? loc : area.Location,
Level = slots.First().LevelMin,
};
}
var encounter = Legal.getStaticLocation(pkm);
if (loc != -1)
encounter.Location = loc;
return encounter;
}
private static int getSuggestedEggMetLocation(PKM pkm)
@ -282,5 +321,18 @@ namespace PKHeX.Core
}
return -1;
}
private static int getSuggestedTransferLocation(PKM pkm)
{
// Return one of legal hatch locations for game
if (pkm.HasOriginalMetLocation)
return -1;
if (pkm.VC1)
return 30013;
if (pkm.Format == 4) // Pal Park
return 0x37;
if (pkm.Format == 5) // Transporter
return 30001;
return -1;
}
}
}

View file

@ -25,6 +25,7 @@ namespace PKHeX.Core
Gender,
EVs,
Language,
Nickname,
Trainer,
IVs,
None,
@ -50,7 +51,7 @@ namespace PKHeX.Core
public bool Flag;
private readonly CheckIdentifier Identifier;
internal CheckResult(CheckIdentifier i) { }
internal CheckResult(CheckIdentifier i) { Identifier = i; }
internal CheckResult(Severity s, string c, CheckIdentifier i)
{
Judgement = s;
@ -115,12 +116,11 @@ namespace PKHeX.Core
return;
}
}
if (pkm.Format < 6)
// abort if specimen wasn't transferred x->6
if (pkm.Format < 6 || !(3 <= pkm.GenNumber && pkm.GenNumber <= 5))
return;
if (pkm.GenNumber >= 6)
return;
// When transferred to Generation 6, the Encryption Constant is copied from the PID.
// The PID is then checked to see if it becomes shiny with the new Shiny rules (>>4 instead of >>3)
// If the PID is nonshiny->shiny, the top bit is flipped.
@ -144,12 +144,12 @@ namespace PKHeX.Core
// If the Pokémon is not nicknamed, it should match one of the language strings.
if (pkm.Nickname.Length == 0)
{
AddLine(Severity.Invalid, "Nickname is empty.", CheckIdentifier.EVs);
AddLine(Severity.Invalid, "Nickname is empty.", CheckIdentifier.Nickname);
return;
}
if (pkm.Species > PKX.SpeciesLang[0].Length)
{
AddLine(Severity.Indeterminate, "Species index invalid for Nickname comparison.", CheckIdentifier.EVs);
AddLine(Severity.Indeterminate, "Species index invalid for Nickname comparison.", CheckIdentifier.Nickname);
return;
}
@ -160,7 +160,7 @@ namespace PKHeX.Core
int lang = Array.IndexOf(PKX.SpeciesLang, langset);
if (pk.Length > (lang == 2 ? 10 : 5))
AddLine(Severity.Invalid, "Nickname too long.", CheckIdentifier.Trainer);
AddLine(Severity.Invalid, "Nickname too long.", CheckIdentifier.Nickname);
}
if (!Encounter.Valid)
@ -194,7 +194,23 @@ namespace PKHeX.Core
else if (pkm.SM)
{
// TODO
AddLine(Severity.Valid, "Ingame Trade for Sun/Moon un-implemented.", CheckIdentifier.EVs);
AddLine(Severity.Valid, "Ingame Trade for Sun/Moon not implemented.", CheckIdentifier.Nickname);
return;
}
else if (pkm.Format <= 2 || pkm.VC)
{
var et = EncounterOriginal as EncounterTrade;
if (et?.TID == 0) // Gen1 Trade
{
string ot = pkm.OT_Name;
string tr = pkm.Format <= 2 ? "TRAINER" : "Trainer"; // decaps on transfer
if (ot != "トレーナー" && ot != tr)
AddLine(Severity.Invalid, "Incorrect OT name for RBY in-game trade.", CheckIdentifier.Trainer);
}
else // Gen2
{
AddLine(Severity.Valid, "Ingame Trade for GSC not implemented.", CheckIdentifier.Trainer);
}
return;
}
@ -213,11 +229,11 @@ namespace PKHeX.Core
string OT = validOT[validOT.Length/2 + index];
if (nick != pkm.Nickname)
AddLine(Severity.Fishy, "Ingame Trade nickname has been altered.", CheckIdentifier.EVs);
AddLine(Severity.Fishy, "Ingame Trade nickname has been altered.", CheckIdentifier.Nickname);
else if (OT != pkm.OT_Name)
AddLine(Severity.Invalid, "Ingame Trade OT has been altered.", CheckIdentifier.Trainer);
else
AddLine(Severity.Valid, "Ingame Trade OT/Nickname have not been altered.", CheckIdentifier.EVs);
AddLine(Severity.Valid, "Ingame Trade OT/Nickname have not been altered.", CheckIdentifier.Nickname);
return;
}
@ -225,11 +241,11 @@ namespace PKHeX.Core
if (pkm.IsEgg)
{
if (!pkm.IsNicknamed && (pkm.Format != 7))
AddLine(Severity.Invalid, "Eggs must be nicknamed.", CheckIdentifier.EVs);
AddLine(Severity.Invalid, "Eggs must be nicknamed.", CheckIdentifier.Egg);
else if (PKX.SpeciesLang[pkm.Language][0] != pkm.Nickname)
AddLine(Severity.Invalid, "Egg name does not match language Egg name.", CheckIdentifier.EVs);
AddLine(Severity.Invalid, "Egg name does not match language Egg name.", CheckIdentifier.Egg);
else
AddLine(Severity.Valid, "Egg matches language Egg name.", CheckIdentifier.EVs);
AddLine(Severity.Valid, "Egg matches language Egg name.", CheckIdentifier.Egg);
return;
}
@ -246,25 +262,31 @@ namespace PKHeX.Core
AddLine(Severity.Fishy, index == pkm.Species && i != pkm.Language
? "Nickname matches another species name (+language)."
: "Nickname flagged, matches species name.", CheckIdentifier.EVs);
: "Nickname flagged, matches species name.", CheckIdentifier.Nickname);
return;
}
AddLine(Severity.Valid, "Nickname does not match another species name.", CheckIdentifier.EVs);
return;
AddLine(Severity.Valid, "Nickname does not match another species name.", CheckIdentifier.Nickname);
}
// else
else if (pkm.Format < 3)
{
// pk1/pk2 IsNicknamed getter checks for match, logic should only reach here if matches.
AddLine(Severity.Valid, "Nickname matches species name.", CheckIdentifier.Nickname);
}
else
{
// Can't have another language name if it hasn't evolved or wasn't a language-traded egg.
bool match = (pkm.WasTradedEgg || Legal.getHasEvolved(pkm)) && PKX.SpeciesLang.Any(lang => lang[pkm.Species] == nickname)
|| PKX.SpeciesLang[pkm.Language][pkm.Species] == nickname;
if (!match)
AddLine(Severity.Invalid, "Nickname does not match species name.", CheckIdentifier.EVs);
{
if ((EncounterMatch as MysteryGift)?.CardID == 2046 && (pkm.SID << 16 | pkm.TID) == 0x79F57B49)
AddLine(Severity.Valid, "Nickname matches demo language name.", CheckIdentifier.Nickname);
else
AddLine(Severity.Invalid, "Nickname does not match species name.", CheckIdentifier.Nickname);
}
else
AddLine(Severity.Valid, "Nickname matches species name.", CheckIdentifier.EVs);
// return;
AddLine(Severity.Valid, "Nickname matches species name.", CheckIdentifier.Nickname);
}
}
private void verifyEVs()
@ -328,6 +350,10 @@ namespace PKHeX.Core
else if (pkm.IVs[0] < 30 && pkm.IVs.All(iv => pkm.IVs[0] == iv))
AddLine(Severity.Fishy, "All IVs are equal.", CheckIdentifier.IVs);
}
private void verifyDVs()
{
// todo
}
private void verifyOT()
{
if (EncounterType == typeof(EncounterTrade))
@ -348,19 +374,21 @@ namespace PKHeX.Core
AddLine(Severity.Fishy, "SID is zero.", CheckIdentifier.Trainer);
if (pkm.VC)
{
string tr = pkm.OT_Name;
string pk = pkm.Nickname;
var langset = PKX.SpeciesLang.FirstOrDefault(s => s.Contains(pk)) ?? PKX.SpeciesLang[2];
int lang = Array.IndexOf(PKX.SpeciesLang, langset);
verifyG1OT();
}
private void verifyG1OT()
{
string tr = pkm.OT_Name;
string pk = pkm.Nickname;
var langset = PKX.SpeciesLang.FirstOrDefault(s => s.Contains(pk)) ?? PKX.SpeciesLang[2];
int lang = Array.IndexOf(PKX.SpeciesLang, langset);
if (tr.Length > (lang == 2 ? 7 : 5))
AddLine(Severity.Invalid, "OT Name too long.", CheckIdentifier.Trainer);
if (pkm.Species == 151)
{
if (tr != "GF" && tr != "ゲーフリ") // if there are more events with special OTs, may be worth refactoring
AddLine(Severity.Invalid, "Incorrect event OT Name.", CheckIdentifier.Trainer);
}
if (tr.Length > (lang == 2 ? 7 : 5))
AddLine(Severity.Invalid, "OT Name too long.", CheckIdentifier.Trainer);
if (pkm.Species == 151)
{
if (tr != "GF" && tr != "ゲーフリ") // if there are more events with special OTs, may be worth refactoring
AddLine(Severity.Invalid, "Incorrect event OT Name.", CheckIdentifier.Trainer);
}
}
@ -386,158 +414,233 @@ namespace PKHeX.Core
}
}
}
private CheckResult verifyEncounterLink()
{
// Should NOT be Fateful, and should be in Database
EncounterLink enc = EncounterMatch as EncounterLink;
if (enc == null)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: unable to find matching gift.", CheckIdentifier.Encounter);
if (pkm.XY && !enc.XY)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in XY.", CheckIdentifier.Encounter);
if (pkm.AO && !enc.ORAS)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in ORAS.", CheckIdentifier.Encounter);
if (pkm.SM && !enc.SM)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in SM.", CheckIdentifier.Encounter);
if (enc.Shiny != null && (bool)enc.Shiny ^ pkm.IsShiny)
return new CheckResult(Severity.Invalid, "Shiny Link gift mismatch.", CheckIdentifier.Encounter);
return pkm.FatefulEncounter
? new CheckResult(Severity.Invalid, "Invalid Link Gift: should not be Fateful Encounter.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid Link gift.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterEvent()
{
MysteryGift MatchedGift = EncounterMatch as MysteryGift;
if (MatchedGift != null)
return new CheckResult(Severity.Valid, $"Matches #{MatchedGift.CardID:0000} ({MatchedGift.CardTitle})", CheckIdentifier.Encounter);
return null;
}
private CheckResult verifyEncounterEgg()
{
// Check Hatch Locations
if (pkm.Met_Level != 1)
return new CheckResult(Severity.Invalid, "Invalid met level, expected 1.", CheckIdentifier.Encounter);
// Check species
if (Legal.NoHatchFromEgg.Contains(pkm.Species))
return new CheckResult(Severity.Invalid, "Species cannot be hatched from an egg.", CheckIdentifier.Encounter);
if (pkm.IsEgg)
{
if (pkm.Egg_Location == 30002)
return new CheckResult(Severity.Invalid, "Egg location shouldn't be 'traded' for an un-hatched egg.", CheckIdentifier.Encounter);
if (pkm.Met_Location == 30002)
return new CheckResult(Severity.Valid, "Valid traded un-hatched egg.", CheckIdentifier.Encounter);
return pkm.Met_Location == 0
? new CheckResult(Severity.Valid, "Valid un-hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid location for un-hatched egg (expected no met location).", CheckIdentifier.Encounter);
}
if (pkm.XY)
{
if (pkm.Egg_Location == 318)
return new CheckResult(Severity.Invalid, "Invalid X/Y egg location.", CheckIdentifier.Encounter);
return Legal.ValidMet_XY.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid X/Y hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid X/Y location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.AO)
{
return Legal.ValidMet_AO.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid OR/AS hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid OR/AS location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.SM)
{
return Legal.ValidMet_SM.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid S/M hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid S/M location for hatched egg.", CheckIdentifier.Encounter);
}
return new CheckResult(Severity.Invalid, "Invalid location for hatched egg.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterSafari()
{
if (pkm.Species == 670 || pkm.Species == 671) // Floette
if (!new[] {0, 1, 3}.Contains(pkm.AltForm)) // 0/1/3 - RBY
return new CheckResult(Severity.Invalid, "Friend Safari: Not valid color.", CheckIdentifier.Encounter);
else if (pkm.Species == 710 || pkm.Species == 711) // Pumpkaboo
if (pkm.AltForm != 1) // Average
return new CheckResult(Severity.Invalid, "Friend Safari: Not average sized.", CheckIdentifier.Encounter);
else if (pkm.Species == 586) // Sawsbuck
if (pkm.AltForm != 0)
return new CheckResult(Severity.Invalid, "Friend Safari: Not Spring form.", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid Friend Safari encounter.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterWild()
{
EncounterSlot[] enc = (EncounterSlot[])EncounterMatch;
if (enc.Any(slot => slot.Normal))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location.", CheckIdentifier.Encounter);
// Decreased Level Encounters
if (enc.Any(slot => slot.WhiteFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (White Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (White Flute).", CheckIdentifier.Encounter);
// Increased Level Encounters
if (enc.Any(slot => slot.BlackFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute).", CheckIdentifier.Encounter);
if (enc.Any(slot => slot.Pressure))
return new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid encounter at location (DexNav).", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterStatic()
{
var s = (EncounterStatic)EncounterMatch;
// Re-parse relearn moves
if (s.EggLocation != 60002 || vRelearn.Any(rl => !rl.Valid))
{
for (int i = 0; i < 4; i++)
vRelearn[i] = pkm.RelearnMoves[i] != s.Relearn[i]
? new CheckResult(Severity.Invalid, "Static encounter relearn move mismatch.", CheckIdentifier.RelearnMove)
: new CheckResult(CheckIdentifier.RelearnMove);
return new CheckResult(Severity.Valid, "Valid gift/static encounter.", CheckIdentifier.Encounter);
}
return null;
}
private CheckResult verifyEncounterTrade()
{
return new CheckResult(Severity.Valid, "Valid ingame trade.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterG1()
{
// Since encounter matching is super weak due to limited stored data in the structure
// Calculate all 3 at the same time and pick the best result (by species).
// Favor special event move gifts as Static Encounters when applicable
var s = Legal.getValidStaticEncounter(pkm, gen1Encounter: true);
var e = Legal.getValidWildEncounters(pkm);
var t = Legal.getValidIngameTrade(pkm, gen1Encounter: true);
const byte invalid = 255;
var sm = s?.Species ?? invalid;
var em = e?.Min(slot => slot.Species) ?? invalid;
var tm = t?.Species ?? invalid;
if (sm + em + tm == 3*invalid)
return new CheckResult(Severity.Invalid, "Unknown encounter.", CheckIdentifier.Encounter);
if (s != null && s.Moves[0] != 0 && pkm.Moves.Contains(s.Moves[0]))
{
EncounterMatch = s;
return verifyEncounterStatic();
}
if (em <= sm && em <= tm)
{
EncounterMatch = e;
return verifyEncounterWild();
}
if (sm <= em && sm <= tm)
{
EncounterMatch = s;
return verifyEncounterStatic();
}
if (tm <= sm && tm <= em)
{
EncounterMatch = t;
return verifyEncounterTrade();
}
// shouldn't ever hit, above 3*invalid check should abort
Console.WriteLine($"Gen1 encounter fallthrough: {pkm.FileName}");
return new CheckResult(Severity.Invalid, "Unknown encounter.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounter()
{
if (pkm.GenNumber < 6)
return new CheckResult(Severity.NotImplemented, "Not Implemented.", CheckIdentifier.Encounter);
if (pkm.VC)
if (pkm.VC || pkm.Format < 3)
{
int baseSpecies = Legal.getBaseSpecies(pkm);
if ((pkm.VC1 && baseSpecies > Legal.MaxSpeciesID_1) ||
(pkm.VC2 && baseSpecies > Legal.MaxSpeciesID_2))
bool g1 = pkm.VC1 || pkm.Format == 1;
if ((g1 && baseSpecies > Legal.MaxSpeciesID_1) || (baseSpecies > Legal.MaxSpeciesID_2))
return new CheckResult(Severity.Invalid, "VC: Unobtainable species.", CheckIdentifier.Encounter);
// Get EncounterMatch prior to parsing transporter legality
var result = verifyEncounterG1();
EncounterOriginal = EncounterMatch;
return verifyVCEncounter(baseSpecies);
if (pkm.Format > 2) // transported to 7+
AddLine(verifyVCEncounter(baseSpecies));
return result;
}
if (pkm.WasLink)
return verifyEncounterLink();
bool wasEvent = pkm.WasEvent || pkm.WasEventEgg;
if (wasEvent)
{
// Should NOT be Fateful, and should be in Database
EncounterLink enc = EncounterMatch as EncounterLink;
if (enc == null)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: unable to find matching gift.", CheckIdentifier.Encounter);
if (pkm.XY && !enc.XY)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in XY.", CheckIdentifier.Encounter);
if (pkm.AO && !enc.ORAS)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in ORAS.", CheckIdentifier.Encounter);
if (pkm.SM && !enc.SM)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in SM.", CheckIdentifier.Encounter);
if (enc.Shiny != null && (bool)enc.Shiny ^ pkm.IsShiny)
return new CheckResult(Severity.Invalid, "Shiny Link gift mismatch.", CheckIdentifier.Encounter);
return pkm.FatefulEncounter
? new CheckResult(Severity.Invalid, "Invalid Link Gift: should not be Fateful Encounter.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid Link gift.", CheckIdentifier.Encounter);
var result = verifyEncounterEvent();
if (result != null)
return result;
}
if (pkm.WasEvent || pkm.WasEventEgg)
if (null != (EncounterMatch = Legal.getValidStaticEncounter(pkm)))
{
MysteryGift MatchedGift = EncounterMatch as MysteryGift;
if (MatchedGift != null)
return new CheckResult(Severity.Valid, $"Matches #{MatchedGift.CardID:0000} ({MatchedGift.CardTitle})", CheckIdentifier.Encounter);
}
var result = verifyEncounterStatic();
if (result != null)
return result;
EncounterMatch = Legal.getValidStaticEncounter(pkm);
if (EncounterMatch != null)
{
// Re-parse relearn moves
var s = (EncounterStatic)EncounterMatch;
if (s.EggLocation != 60002 || vRelearn.Any(rl => !rl.Valid))
{
for (int i = 0; i < 4; i++)
vRelearn[i] = pkm.RelearnMoves[i] != s.Relearn[i]
? new CheckResult(Severity.Invalid, "Static encounter relearn move mismatch.", CheckIdentifier.RelearnMove)
: new CheckResult(CheckIdentifier.RelearnMove);
return new CheckResult(Severity.Valid, "Valid gift/static encounter.", CheckIdentifier.Encounter);
}
EncounterMatch = null; // Reset Encounter Object, test for remaining encounters
}
EncounterMatch = null; // Reset object
if (pkm.WasEgg)
{
// Check Hatch Locations
if (pkm.Met_Level != 1)
return new CheckResult(Severity.Invalid, "Invalid met level, expected 1.", CheckIdentifier.Encounter);
// Check species
if (Legal.NoHatchFromEgg.Contains(pkm.Species))
return new CheckResult(Severity.Invalid, "Species cannot be hatched from an egg.", CheckIdentifier.Encounter);
if (pkm.IsEgg)
{
if (pkm.Egg_Location == 30002)
return new CheckResult(Severity.Invalid, "Egg location shouldn't be 'traded' for an un-hatched egg.", CheckIdentifier.Encounter);
return verifyEncounterEgg();
if (null != (EncounterMatch = Legal.getValidFriendSafari(pkm)))
return verifyEncounterSafari();
if (null != (EncounterMatch = Legal.getValidWildEncounters(pkm)))
return verifyEncounterWild();
if (null != (EncounterMatch = Legal.getValidIngameTrade(pkm)))
return verifyEncounterTrade();
if (pkm.Met_Location == 30002)
return new CheckResult(Severity.Valid, "Valid traded un-hatched egg.", CheckIdentifier.Encounter);
return pkm.Met_Location == 0
? new CheckResult(Severity.Valid, "Valid un-hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid location for un-hatched egg (expected no met location).", CheckIdentifier.Encounter);
}
if (pkm.XY)
{
if (pkm.Egg_Location == 318)
return new CheckResult(Severity.Invalid, "Invalid X/Y egg location.", CheckIdentifier.Encounter);
return Legal.ValidMet_XY.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid X/Y hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid X/Y location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.AO)
{
return Legal.ValidMet_AO.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid OR/AS hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid OR/AS location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.SM)
{
return Legal.ValidMet_SM.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid S/M hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid S/M location for hatched egg.", CheckIdentifier.Encounter);
}
return new CheckResult(Severity.Invalid, "Invalid location for hatched egg.", CheckIdentifier.Encounter);
}
EncounterMatch = Legal.getValidFriendSafari(pkm);
if (EncounterMatch != null)
{
if (pkm.Species == 670 || pkm.Species == 671) // Floette
if (!new[] {0, 1, 3}.Contains(pkm.AltForm)) // 0/1/3 - RBY
return new CheckResult(Severity.Invalid, "Friend Safari: Not valid color.", CheckIdentifier.Encounter);
else if (pkm.Species == 710 || pkm.Species == 711) // Pumpkaboo
if (pkm.AltForm != 1) // Average
return new CheckResult(Severity.Invalid, "Friend Safari: Not average sized.", CheckIdentifier.Encounter);
else if (pkm.Species == 586) // Sawsbuck
if (pkm.AltForm != 0)
return new CheckResult(Severity.Invalid, "Friend Safari: Not Spring form.", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid Friend Safari encounter.", CheckIdentifier.Encounter);
}
EncounterMatch = Legal.getValidWildEncounters(pkm);
if (EncounterMatch != null)
{
EncounterSlot[] enc = (EncounterSlot[])EncounterMatch;
if (enc.Any(slot => slot.Normal))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location.", CheckIdentifier.Encounter);
// Decreased Level Encounters
if (enc.Any(slot => slot.WhiteFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (White Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (White Flute).", CheckIdentifier.Encounter);
// Increased Level Encounters
if (enc.Any(slot => slot.BlackFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute).", CheckIdentifier.Encounter);
if (enc.Any(slot => slot.Pressure))
return new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid encounter at location (DexNav).", CheckIdentifier.Encounter);
}
EncounterMatch = Legal.getValidIngameTrade(pkm);
if (EncounterMatch != null)
return new CheckResult(Severity.Valid, "Valid ingame trade.", CheckIdentifier.Encounter);
if (pkm.WasEvent || pkm.WasEventEgg)
return new CheckResult(Severity.Invalid, "Unable to match to a Mystery Gift in the database.", CheckIdentifier.Encounter);
return new CheckResult(Severity.Invalid, "Unknown encounter.", CheckIdentifier.Encounter);
return wasEvent
? new CheckResult(Severity.Invalid, "Unable to match to a Mystery Gift in the database.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Unknown encounter.", CheckIdentifier.Encounter);
}
private CheckResult verifyVCEncounter(int baseSpecies)
{
@ -546,6 +649,19 @@ namespace PKHeX.Core
if ((pkm.VC1 && species > Legal.MaxSpeciesID_1) ||
(pkm.VC2 && species > Legal.MaxSpeciesID_2))
species = baseSpecies;
// Check existing EncounterMatch
if ((EncounterOriginal ?? EncounterMatch) == null)
return new CheckResult(Severity.Invalid, "Unable to match an encounter from origin game.", CheckIdentifier.Encounter);
var s = EncounterMatch as EncounterStatic;
if (s != null && s.Version == GameVersion.SPECIAL)
{
bool exceptions = false;
exceptions |= baseSpecies == 151 && pkm.TID == 22796;
if (!exceptions)
AddLine(new CheckResult(Severity.Invalid, "Special encounter is not available to Virtual Console games.", CheckIdentifier.Encounter));
}
EncounterMatch = new EncounterStatic
{
@ -556,6 +672,7 @@ namespace PKHeX.Core
Fateful = species == 151,
Location = 30013,
EggLocation = 0,
Version = GameVersion.RBY
};
var ematch = (EncounterStatic) EncounterMatch;
@ -564,8 +681,12 @@ namespace PKHeX.Core
if (pkm.Egg_Location != ematch.EggLocation)
return new CheckResult(Severity.Invalid, "Should not have an egg location.", CheckIdentifier.Encounter);
if (species == 150 && pkm.Moves.Contains(6))
return new CheckResult(Severity.Invalid, "Mewtwo cannot be transferred while knowing Pay Day.", CheckIdentifier.Encounter);
return new CheckResult(CheckIdentifier.Encounter);
}
private void verifyLevel()
{
MysteryGift MatchedGift = EncounterMatch as MysteryGift;
@ -832,13 +953,26 @@ namespace PKHeX.Core
}
}
if (pkm.GenNumber >= 6 && abilities[pkm.AbilityNumber >> 1] != pkm.Ability)
AddLine(Severity.Invalid, "Ability does not match ability number.", CheckIdentifier.Ability);
else if (pkm.GenNumber <= 5 && pkm.Version != (int)GameVersion.CXD && abilities[0] != abilities[1] && pkm.PIDAbility != abilval)
AddLine(Severity.Invalid, "Ability does not match PID.", CheckIdentifier.Ability);
if (3 <= pkm.Format && pkm.Format <= 5) // 3-5
{
if (pkm.Version != (int) GameVersion.CXD && abilities[0] != abilities[1] && pkm.PIDAbility != abilval)
{
AddLine(Severity.Invalid, "Ability does not match PID.", CheckIdentifier.Ability);
return;
}
}
else
AddLine(Severity.Valid, "Ability matches ability number.", CheckIdentifier.Ability);
{
if (abilities[pkm.AbilityNumber >> 1] != pkm.Ability)
{
AddLine(Severity.Invalid, "Ability does not match ability number.", CheckIdentifier.Ability);
return;
}
}
AddLine(Severity.Valid, "Ability matches ability number.", CheckIdentifier.Ability);
}
private void verifyBall()
{
if (pkm.GenNumber < 6)
@ -914,29 +1048,8 @@ namespace PKHeX.Core
if (pkm.WasEgg)
{
if (pkm.GenNumber < 6) // No inheriting Balls
{
if (pkm.Ball != 0x04)
AddLine(Severity.Invalid, "Ball should be Pokéball.", CheckIdentifier.Ball);
return;
}
if (pkm.Ball == 0x01) // Master Ball
{ AddLine(Severity.Invalid, "Master Ball on egg origin.", CheckIdentifier.Ball); return; }
if (pkm.Ball == 0x10) // Cherish Ball
{ AddLine(Severity.Invalid, "Cherish Ball on non-event.", CheckIdentifier.Ball); return; }
if (pkm.Ball == 0x04) // Poké Ball
{ AddLine(Severity.Valid, "Standard Poké Ball.", CheckIdentifier.Ball); return; }
switch (pkm.GenNumber)
{
case 6: // Gen6 Inheritance Rules
verifyEggBallGen6();
return;
case 7: // Gen7 Inheritance Rules
verifyEggBallGen7();
return;
}
verifyBallEgg();
return;
}
if (pkm.Ball == 0x04) // Poké Ball
@ -947,7 +1060,35 @@ namespace PKHeX.Core
AddLine(Severity.Invalid, "No ball check satisfied, assuming illegal.", CheckIdentifier.Ball);
}
private void verifyEggBallGen6()
private void verifyBallEgg()
{
if (pkm.GenNumber < 6) // No inheriting Balls
{
if (pkm.Ball != 0x04) // Must be Pokéball -- no ball inheritance.
AddLine(Severity.Invalid, "Ball should be Pokéball.", CheckIdentifier.Ball);
else
AddLine(Severity.Valid, "Pokéball on egg.", CheckIdentifier.Ball);
return;
}
if (pkm.Ball == 0x01) // Master Ball
{ AddLine(Severity.Invalid, "Master Ball on egg origin.", CheckIdentifier.Ball); return; }
if (pkm.Ball == 0x10) // Cherish Ball
{ AddLine(Severity.Invalid, "Cherish Ball on non-event.", CheckIdentifier.Ball); return; }
if (pkm.Ball == 0x04) // Poké Ball
{ AddLine(Severity.Valid, "Standard Poké Ball.", CheckIdentifier.Ball); return; }
switch (pkm.GenNumber)
{
case 6: // Gen6 Inheritance Rules
verifyBallEggGen6();
return;
case 7: // Gen7 Inheritance Rules
verifyBallEggGen7();
return;
}
}
private void verifyBallEggGen6()
{
if (pkm.Gender == 2) // Genderless
{
@ -1047,8 +1188,7 @@ namespace PKHeX.Core
? "Ball unobtainable in origin generation."
: "No ball check satisfied, assuming illegal.", CheckIdentifier.Ball);
}
private void verifyEggBallGen7()
private void verifyBallEggGen7()
{
var Lineage = Legal.getLineage(pkm).ToArray();
if (722 <= pkm.Species && pkm.Species <= 730) // G7 Starters
@ -1111,17 +1251,10 @@ namespace PKHeX.Core
}
if (0x02 <= pkm.Ball && pkm.Ball <= 0x0C) // Don't worry, Ball # 0x05 was already checked.
{
if (Legal.Ban_Gen3Ball_AllowG7.Contains(pkm.Species))
{
if (pkm.AbilityNumber == 4)
AddLine(Severity.Invalid, "Ball not possible for species with hidden ability.", CheckIdentifier.Ball);
else
AddLine(Severity.Valid, "Obtainable capture for Gen3 Ball.", CheckIdentifier.Ball);
}
else if (Legal.Ban_Gen3Ball.Contains(pkm.Species))
if (Legal.Ban_Gen3Ball_7.Contains(pkm.Species))
AddLine(Severity.Invalid, "Unobtainable capture for Gen3 Ball.", CheckIdentifier.Ball);
else
AddLine(Severity.Valid, "Obtainable capture for Gen3Ball.", CheckIdentifier.Ball);
AddLine(Severity.Valid, "Obtainable capture for Gen3 Ball.", CheckIdentifier.Ball);
return;
}
@ -1154,6 +1287,7 @@ namespace PKHeX.Core
? "Ball unobtainable in origin generation."
: "No ball check satisfied, assuming illegal.", CheckIdentifier.Ball);
}
private CheckResult verifyHistory()
{
if (!Encounter.Valid)
@ -1255,7 +1389,7 @@ namespace PKHeX.Core
if (pkm.Species != 350) // Milotic
return new CheckResult(Severity.Invalid, "Untraded -- requires a trade evolution.", CheckIdentifier.History);
if (pkm.CNT_Beauty < 170) // Beauty Contest Stat Requirement
return new CheckResult(Severity.Invalid, "Untraded -- Beauty is not high enough for Levelup Evolution.", CheckIdentifier.History);
return new CheckResult(Severity.Invalid, "Untraded -- Beauty is not high enough for Level-up Evolution.", CheckIdentifier.History);
if (pkm.CurrentLevel == 1)
return new CheckResult(Severity.Invalid, "Untraded -- Beauty is high enough but still Level 1.", CheckIdentifier.History);
}
@ -1286,14 +1420,14 @@ namespace PKHeX.Core
if (pkm.Species != 350) // Milotic
return new CheckResult(Severity.Invalid, "Untraded -- requires a trade evolution.", CheckIdentifier.History);
if (pkm.CNT_Beauty < 170) // Beauty Contest Stat Requirement
return new CheckResult(Severity.Invalid, "Untraded -- Beauty is not high enough for Levelup Evolution.", CheckIdentifier.History);
return new CheckResult(Severity.Invalid, "Untraded -- Beauty is not high enough for Level-up Evolution.", CheckIdentifier.History);
if (pkm.CurrentLevel == 1)
return new CheckResult(Severity.Invalid, "Untraded -- Beauty is high enough but still Level 1.", CheckIdentifier.History);
}
}
else // Is Traded
{
if (pkm.HT_Memory == 0 && pkm.Format == 6)
if (pkm.Format == 6 && pkm.HT_Memory == 0)
return new CheckResult(Severity.Invalid, "Memory -- missing Handling Trainer Memory.", CheckIdentifier.History);
}
@ -1335,27 +1469,22 @@ namespace PKHeX.Core
break;
}
int matchingMoveMemory = Array.IndexOf(Legal.MoveSpecificMemories[0], m);
if (matchingMoveMemory != -1 && pkm.Species != 235 && !Legal.getCanLearnMachineMove(pkm, Legal.MoveSpecificMemories[1][matchingMoveMemory]))
{
if (matchingMoveMemory != -1 && pkm.Species != 235 && !Legal.getCanLearnMachineMove(pkm, Legal.MoveSpecificMemories[1][matchingMoveMemory], 6))
return new CheckResult(Severity.Invalid, resultPrefix + "Memory: Species cannot learn this move.", CheckIdentifier.Memory);
}
if (m == 6 && !Legal.LocationsWithPKCenter[0].Contains(t))
{
return new CheckResult(Severity.Invalid, resultPrefix + "Memory: Location doesn't have a Pokemon Center.", CheckIdentifier.Memory);
}
if (m == 21) // {0} saw {2} carrying {1} on its back. {4} that {3}.
{
if (!Legal.getCanLearnMachineMove(new PK6 {Species = t, EXP = PKX.getEXP(100, t)}, 19))
if (!Legal.getCanLearnMachineMove(new PK6 {Species = t, EXP = PKX.getEXP(100, t)}, 19, 6))
return new CheckResult(Severity.Invalid, resultPrefix + "Memory: Argument Species cannot learn Fly.", CheckIdentifier.Memory);
}
if ((m == 16 || m == 48) && (t == 0 || !Legal.getCanKnowMove(pkm, t, GameVersion.Any)))
{
if ((m == 16 || m == 48) && (t == 0 || !Legal.getCanKnowMove(pkm, t, 6)))
return new CheckResult(Severity.Invalid, resultPrefix + "Memory: Species cannot know this move.", CheckIdentifier.Memory);
}
if (m == 49 && (t == 0 || !Legal.getCanRelearnMove(pkm, t, GameVersion.Any))) // {0} was able to remember {2} at {1}'s instruction. {4} that {3}.
{
if (m == 49 && (t == 0 || !Legal.getCanRelearnMove(pkm, t, 6))) // {0} was able to remember {2} at {1}'s instruction. {4} that {3}.
return new CheckResult(Severity.Invalid, resultPrefix + "Memory: Species cannot relearn this move.", CheckIdentifier.Memory);
}
return new CheckResult(Severity.Valid, resultPrefix + "Memory is valid.", CheckIdentifier.Memory);
}
private void verifyOTMemory()
@ -1466,7 +1595,7 @@ namespace PKHeX.Core
if (!History.Valid)
return;
if (pkm.GenNumber == 7)
if (pkm.GenNumber == 7 || pkm.GenNumber == 1)
{
bool check = pkm.VC1 || pkm.HT_Memory != 0;
if (!check)
@ -1574,11 +1703,10 @@ namespace PKHeX.Core
case 25: // Pikachu
if (pkm.Format == 6 && pkm.AltForm != 0 ^ EncounterType == typeof(EncounterStatic))
{
if (EncounterType == typeof(EncounterStatic))
AddLine(Severity.Invalid, "Cosplay Pikachu cannot have the default form.", CheckIdentifier.Form);
else
AddLine(Severity.Invalid, "Only Cosplay Pikachu can have this form.", CheckIdentifier.Form);
string msg = EncounterType == typeof (EncounterStatic)
? "Cosplay Pikachu cannot have the default form."
: "Only Cosplay Pikachu can have this form.";
AddLine(Severity.Invalid, msg, CheckIdentifier.Form);
return;
}
if (pkm.Format == 7 && pkm.AltForm != 0 ^ EncounterIsMysteryGift)
@ -1612,6 +1740,19 @@ namespace PKHeX.Core
AddLine(Severity.Valid, "Held item matches Form.", CheckIdentifier.Form);
}
break;
case 649: // Genesect
{
int item = pkm.HeldItem;
int form = 0;
if (116 <= item && item <= 119)
form = item - 115;
if (form != pkm.AltForm)
AddLine(Severity.Invalid, "Held item does not match Form.", CheckIdentifier.Form);
else
AddLine(Severity.Valid, "Held item matches Form.", CheckIdentifier.Form);
}
break;
case 658: // Greninja
if (pkm.AltForm > 1) // Ash Battle Bond active
{
@ -1784,10 +1925,10 @@ namespace PKHeX.Core
CheckResult[] res = new CheckResult[4];
for (int i = 0; i < 4; i++)
res[i] = new CheckResult(CheckIdentifier.Move);
if (pkm.GenNumber < 6 || pkm.VC1)
return res;
var validMoves = Legal.getValidMoves(pkm, EvoChain).ToArray();
var validMoves = Legal.getValidMoves(pkm, EvoChainsAllGens, Tutor: false, Machine: false).ToArray();
var validTMHM = Legal.getValidMoves(pkm, EvoChainsAllGens, Tutor: false, MoveReminder: false).ToArray();
var validTutor = Legal.getValidMoves(pkm, EvoChainsAllGens, Machine: false, MoveReminder: false).ToArray();
if (pkm.Species == 235) // Smeargle
{
for (int i = 0; i < 4; i++)
@ -1800,19 +1941,8 @@ namespace PKHeX.Core
int[] RelearnMoves = pkm.RelearnMoves;
foreach (MysteryGift mg in EventGiftMatch)
{
for (int i = 0; i < 4; i++)
{
if (Moves[i] == Legal.Struggle)
res[i] = new CheckResult(Severity.Invalid, "Invalid Move: Struggle.", CheckIdentifier.Move);
else if (validMoves.Contains(Moves[i]))
res[i] = new CheckResult(Severity.Valid, Moves[i] == 0 ? "Empty" : "Level-up.", CheckIdentifier.Move);
else if (RelearnMoves.Contains(Moves[i]))
res[i] = new CheckResult(Severity.Valid, Moves[i] == 0 ? "Empty" : "Relearn Move.", CheckIdentifier.Move) { Flag = true };
else if (mg.Moves.Contains(Moves[i]))
res[i] = new CheckResult(Severity.Valid, "Wonder Card Non-Relearn Move.", CheckIdentifier.Move);
else
res[i] = new CheckResult(Severity.Invalid, "Invalid Move.", CheckIdentifier.Move);
}
int[] SpecialMoves = mg.Moves;
res = parseMoves(Moves, validMoves, RelearnMoves, validTMHM, validTutor, SpecialMoves);
if (res.Any(r => !r.Valid))
continue;
@ -1824,21 +1954,12 @@ namespace PKHeX.Core
else
{
int[] RelearnMoves = pkm.RelearnMoves;
MysteryGift MatchedGift = EncounterMatch as MysteryGift;
int[] GiftMoves = MatchedGift?.Moves ?? new int[0];
for (int i = 0; i < 4; i++)
{
if (Moves[i] == Legal.Struggle)
res[i] = new CheckResult(Severity.Invalid, "Invalid Move: Struggle.", CheckIdentifier.Move);
else if (validMoves.Contains(Moves[i]))
res[i] = new CheckResult(Severity.Valid, Moves[i] == 0 ? "Empty" : "Level-up.", CheckIdentifier.Move);
else if (RelearnMoves.Contains(Moves[i]))
res[i] = new CheckResult(Severity.Valid, Moves[i] == 0 ? "Empty" : "Relearn Move.", CheckIdentifier.Move) { Flag = true };
else if (GiftMoves.Contains(Moves[i]))
res[i] = new CheckResult(Severity.Valid, "Wonder Card Non-Relearn Move.", CheckIdentifier.Move);
else
res[i] = new CheckResult(Severity.Invalid, "Invalid Move.", CheckIdentifier.Move);
}
int[] SpecialMoves = (EncounterMatch as MysteryGift)?.Moves ??
(EncounterMatch as EncounterStatic)?.Moves ??
(EncounterMatch as EncounterTrade)?.Moves ??
new int[0];
res = parseMoves(Moves, validMoves, RelearnMoves, validTMHM, validTutor, SpecialMoves);
}
if (Moves[0] == 0) // None
res[0] = new CheckResult(Severity.Invalid, "Invalid Move.", CheckIdentifier.Move);
@ -1854,6 +1975,28 @@ namespace PKHeX.Core
return res;
}
private static CheckResult[] parseMoves(int[] moves, int[] learn, int[] relearn, int[] tmhm, int[] tutor, int[] special)
{
CheckResult[] res = new CheckResult[4];
for (int i = 0; i < 4; i++)
{
if (moves[i] == 0)
res[i] = new CheckResult(Severity.Valid, "Empty", CheckIdentifier.Move);
else if (learn.Contains(moves[i]))
res[i] = new CheckResult(Severity.Valid, "Level-up.", CheckIdentifier.Move);
else if (relearn.Contains(moves[i]))
res[i] = new CheckResult(Severity.Valid, "Relearn Move.", CheckIdentifier.Move) { Flag = true };
else if (tmhm.Contains(moves[i]))
res[i] = new CheckResult(Severity.Valid, "TM/HM.", CheckIdentifier.Move);
else if (tutor.Contains(moves[i]))
res[i] = new CheckResult(Severity.Valid, "Tutor.", CheckIdentifier.Move);
else if (special.Contains(moves[i]))
res[i] = new CheckResult(Severity.Valid, "Special Non-Relearn Move.", CheckIdentifier.Move);
else
res[i] = new CheckResult(Severity.Invalid, "Invalid Move.", CheckIdentifier.Move);
}
return res;
}
private CheckResult[] verifyRelearn()
{
RelearnBase = null;
@ -1907,7 +2050,7 @@ namespace PKHeX.Core
if (pkm.WasEgg && !Legal.NoHatchFromEgg.Contains(pkm.Species))
{
GameVersion[] Games = { GameVersion.XY };
GameVersion[] Games = {};
switch (pkm.GenNumber)
{
case 6:

File diff suppressed because it is too large Load diff

View file

@ -4,12 +4,14 @@
{
public int Species;
public int Level;
public int MinLevel;
public bool RequiresLvlUp;
public int Form = -1;
public int Flag = -1;
public DexLevel Copy(int lvl)
{
return new DexLevel {Species = Species, Level = lvl, Form = Form, Flag = -1};
return new DexLevel {Species = Species, Level = lvl, MinLevel = MinLevel, RequiresLvlUp = RequiresLvlUp, Form = Form, Flag = -1};
}
public bool Matches(int species, int form)
{

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
@ -24,6 +25,117 @@ namespace PKHeX.Core
};
}
}
private static EncounterSlot1[] getSlots1_GW(byte[] data, ref int ofs, SlotType t)
{
int rate = data[ofs++];
return rate == 0 ? new EncounterSlot1[0] : readSlots(data, ref ofs, 10, t, rate);
}
private static EncounterSlot1[] getSlots1_F(byte[] data, ref int ofs)
{
int count = data[ofs++];
return readSlots(data, ref ofs, count, SlotType.Super_Rod, -1);
}
/// <summary>
/// RBY Format Slot Getter from data.
/// </summary>
/// <param name="data">Byte array containing complete slot data table.</param>
/// <param name="ofs">Offset to start reading from.</param>
/// <param name="count">Amount of slots to read.</param>
/// <param name="t">Type of encounter slot.</param>
/// <param name="rate">Slot type encounter rate.</param>
/// <returns>Array of encounter slots.</returns>
private static EncounterSlot1[] readSlots(byte[] data, ref int ofs, int count, SlotType t, int rate)
{
EncounterSlot1[] slots = new EncounterSlot1[count];
for (int i = 0; i < count; i++)
{
int lvl = data[ofs++];
int spec = data[ofs++];
slots[i] = new EncounterSlot1
{
LevelMax = lvl,
LevelMin = lvl,
Species = spec,
Type = t,
Rate = rate,
};
}
return slots;
}
public static EncounterArea[] getArray1_GW(byte[] data)
{
// RBY Format
var ptr = new int[255];
int count = 0;
for (int i = 0; i < ptr.Length; i++)
{
ptr[i] = BitConverter.ToInt16(data, i*2);
if (ptr[i] != -1)
continue;
count = i;
break;
}
EncounterArea[] areas = new EncounterArea[count];
for (int i = 0; i < areas.Length; i++)
{
var grass = getSlots1_GW(data, ref ptr[i], SlotType.Grass);
var water = getSlots1_GW(data, ref ptr[i], SlotType.Surf);
areas[i] = new EncounterArea
{
Location = i,
Slots = grass.Concat(water).ToArray()
};
}
return areas.Where(area => area.Slots.Any()).ToArray();
}
public static EncounterArea[] getArray1_FY(byte[] data)
{
const int size = 9;
int count = data.Length/size;
EncounterArea[] areas = new EncounterArea[count];
for (int i = 0; i < count; i++)
{
int ofs = i*size + 1;
areas[i] = new EncounterArea
{
Location = data[i*size + 0],
Slots = readSlots(data, ref ofs, 4, SlotType.Super_Rod, -1)
};
}
return areas;
}
public static EncounterArea[] getArray1_F(byte[] data)
{
var ptr = new int[255];
var map = new int[255];
int count = 0;
for (int i = 0; i < ptr.Length; i++)
{
map[i] = data[i*3 + 0];
if (map[i] == 0xFF)
{
count = i;
break;
}
ptr[i] = BitConverter.ToInt16(data, i * 3 + 1);
}
EncounterArea[] areas = new EncounterArea[count];
for (int i = 0; i < areas.Length; i++)
{
areas[i] = new EncounterArea
{
Location = map[i],
Slots = getSlots1_F(data, ref ptr[i])
};
}
return areas;
}
public static EncounterArea[] getArray(byte[][] entries)
{
if (entries == null)
@ -35,5 +147,4 @@ namespace PKHeX.Core
return data;
}
}
}

View file

@ -25,6 +25,28 @@
Pressure = template.Pressure;
}
public string Name => "Wild Encounter";
public string Name
{
get
{
const string wild = "Wild Encounter";
if (Type == SlotType.Any)
return wild;
return wild + " " + $"{Type.ToString().Replace("_", " ")}";
}
}
}
public class EncounterSlot1 : EncounterSlot
{
public int Rate;
public EncounterSlot1() { }
public EncounterSlot1(EncounterSlot1 template)
{
Species = template.Species;
LevelMax = template.LevelMax;
LevelMin = template.LevelMin;
Type = template.Type;
Rate = template.Rate;
}
}
}

View file

@ -10,6 +10,7 @@
public int Form = 0;
public bool? Shiny = null; // false = never, true = always, null = possible
public int[] Relearn = new int[4];
public int[] Moves = new int[4];
public int Gender = -1;
public int EggLocation = 0;
public Nature Nature = Nature.Random;
@ -25,6 +26,15 @@
public bool RibbonWishing = false;
public bool SkipFormCheck = false;
public string Name => "Static Encounter";
public string Name
{
get
{
const string game = "Static Encounter";
if (Version == GameVersion.Any)
return game;
return game + " " + $"({Version})";
}
}
}
}

View file

@ -19,12 +19,18 @@ namespace PKHeX.Core
MaxSpeciesTree = maxSpeciesTree;
switch (game)
{
case GameVersion.SM:
Entries.AddRange(data.Select(d => new EvolutionSet7(d)));
case GameVersion.RBY:
Entries = EvolutionSet1.getArray(data[0], maxSpeciesTree);
break;
case GameVersion.GSC:
Entries = EvolutionSet2.getArray(data[0], maxSpeciesTree);
break;
case GameVersion.ORAS:
Entries.AddRange(data.Select(d => new EvolutionSet6(d)));
break;
case GameVersion.SM:
Entries.AddRange(data.Select(d => new EvolutionSet7(d)));
break;
}
// Create Lineages
@ -32,7 +38,7 @@ namespace PKHeX.Core
for (int i = 0; i < Entries.Count; i++)
Lineage[i] = new EvolutionLineage();
if (Game == GameVersion.ORAS)
Array.Resize(ref Lineage, maxSpeciesTree + 1);
Array.Resize(ref Lineage, MaxSpeciesTree + 1);
// Populate Lineages
for (int i = 1; i < Lineage.Length; i++)
@ -162,6 +168,94 @@ namespace PKHeX.Core
{
public EvolutionMethod[] PossibleEvolutions;
}
public class EvolutionSet1 : EvolutionSet
{
private static EvolutionMethod getMethod(byte[] data, ref int offset)
{
switch (data[offset])
{
case 1: // Level
var m1 = new EvolutionMethod
{
Method = 1, // Level Up
Level = data[offset + 1],
Species = data[offset + 2]
};
offset += 3;
return m1;
case 2: // Use Item
var m2 = new EvolutionMethod
{
Method = 8, // Use Item
Argument = data[offset + 1],
// 1
Species = data[offset + 3],
};
offset += 4;
return m2;
case 3: // Trade
var m3 = new EvolutionMethod
{
Method = 5, // Trade
// 1
Species = data[offset + 2]
};
offset += 3;
return m3;
}
return null;
}
public static List<EvolutionSet> getArray(byte[] data, int maxSpecies)
{
var evos = new List<EvolutionSet>();
int offset = 0;
for (int i = 0; i <= maxSpecies; i++)
{
var m = new List<EvolutionMethod>();
while (data[offset] != 0)
m.Add(getMethod(data, ref offset));
++offset;
evos.Add(new EvolutionSet1 { PossibleEvolutions = m.ToArray() });
}
return evos;
}
}
public class EvolutionSet2 : EvolutionSet
{
private static EvolutionMethod getMethod(byte[] data, ref int offset)
{
int method = data[offset];
int arg = data[offset + 1];
int species = data[offset + 2];
offset += 3;
switch (method)
{
case 1: /* Level Up */ return new EvolutionMethod { Method = 1, Species = species, Level = arg };
case 2: /* Use Item */ return new EvolutionMethod { Method = 8, Species = species, Argument = arg };
case 3: /* Trade */ return new EvolutionMethod { Method = 5, Species = species };
case 4: /*Friendship*/ return new EvolutionMethod { Method = 1, Species = species };
case 5: /* Stats */
// species is currently stat ID, we don't care about evo type as stats can be changed after evo
return new EvolutionMethod { Method = 1, Species = data[offset++], Level = arg }; // Tyrogue stats
}
return null;
}
public static List<EvolutionSet> getArray(byte[] data, int maxSpecies)
{
var evos = new List<EvolutionSet>();
int offset = 0;
for (int i = 0; i <= maxSpecies; i++)
{
var m = new List<EvolutionMethod>();
while (data[offset] != 0)
m.Add(getMethod(data, ref offset));
++offset;
evos.Add(new EvolutionSet2 { PossibleEvolutions = m.ToArray() });
}
return evos;
}
}
public class EvolutionSet6 : EvolutionSet
{
private const int SIZE = 6;
@ -172,7 +266,7 @@ namespace PKHeX.Core
PossibleEvolutions = new EvolutionMethod[data.Length / SIZE];
for (int i = 0; i < data.Length; i += SIZE)
{
PossibleEvolutions[i/SIZE] = new EvolutionMethod
var evo = new EvolutionMethod
{
Method = BitConverter.ToUInt16(data, i + 0),
Argument = BitConverter.ToUInt16(data, i + 2),
@ -183,8 +277,10 @@ namespace PKHeX.Core
};
// Argument is used by both Level argument and Item/Move/etc. Clear if appropriate.
if (argEvos.Contains(PossibleEvolutions[i/SIZE].Method))
PossibleEvolutions[i/SIZE].Level = 0;
if (argEvos.Contains(evo.Method))
evo.Level = 0;
PossibleEvolutions[i/SIZE] = evo;
}
}
}
@ -359,6 +455,7 @@ namespace PKHeX.Core
continue;
oneValid = true;
updateMinValues(dl, evo);
int species = evo.Species;
// Gen7 Personal Formes -- unmap the forme personal entry ID to the actual species ID since species are consecutive
@ -366,7 +463,7 @@ namespace PKHeX.Core
species = pkm.Species - Chain.Count + i;
dl.Add(evo.GetDexLevel(species, lvl));
if (evo.RequiresLevelUp)
lvl--;
break;
@ -379,8 +476,43 @@ namespace PKHeX.Core
if (dl.Any(d => d.Species <= maxSpeciesOrigin) && dl.Last().Species > maxSpeciesOrigin)
dl.RemoveAt(dl.Count - 1);
// Last species is the wild/hatched species, the minimum is 1 because it has not evolved from previous species
dl.Last().MinLevel = 1;
dl.Last().RequiresLvlUp = false;
return dl;
}
private static void updateMinValues(IReadOnlyCollection<DexLevel> dl, EvolutionMethod evo)
{
var last = dl.Last();
if (evo.Level == 0) // Evolutions like elemental stones, trade, etc
{
if (!evo.RequiresLevelUp)
last.MinLevel = 1;
else
{
// Evolutions like frienship, pichu -> pikachu, eevee -> umbreon, etc
last.MinLevel = 2;
var first = dl.First();
if (dl.Count > 1 && !first.RequiresLvlUp)
first.MinLevel = 2; // Raichu from Pikachu would have minimum level 1, but with Pichu included Raichu minimum level is 2
}
}
else // level up evolutions
{
last.MinLevel = evo.Level;
var first = dl.First();
if (dl.Count > 1)
{
if (first.MinLevel < evo.Level && !first.RequiresLvlUp)
first.MinLevel = evo.Level; // Pokemon like Nidoqueen, its minimum level is Nidorina minimum level
if (first.MinLevel <= evo.Level && first.RequiresLvlUp)
first.MinLevel = evo.Level + 1; // Pokemon like Crobat, its minimum level is Golbat minimum level + 1
}
}
last.RequiresLvlUp = evo.RequiresLevelUp;
}
}
public class EvolutionStage
{

View file

@ -1,4 +1,5 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace PKHeX.Core
@ -17,6 +18,35 @@ namespace PKHeX.Core
return Moves;
}
}
public class Learnset1 : Learnset
{
private Learnset1(byte[] data, ref int offset)
{
var moves = new List<int>();
var levels = new List<int>();
while (data[offset] != 0)
{
levels.Add(data[offset++]);
moves.Add(data[offset++]);
}
++offset;
Moves = moves.ToArray();
Levels = levels.ToArray();
Count = Moves.Length;
}
public static Learnset[] getArray(byte[] input, int maxSpecies)
{
var data = new Learnset[maxSpecies + 1];
int offset = 0;
for (int s = 0; s < data.Length; s++)
data[s] = new Learnset1(input, ref offset);
return data;
}
}
public class Learnset6 : Learnset
{
private Learnset6(byte[] data)

View file

@ -1,4 +1,6 @@
namespace PKHeX.Core
using System.Linq;
namespace PKHeX.Core
{
public static partial class Legal
{
@ -197,6 +199,7 @@
695, 696, 697, 698, 699, 700, 701, 702, 703,
719,
};
internal static readonly int[] InvalidSketch = new[] { 165, 448 }.Concat(Z_Moves).ToArray(); // Struggle & Chatter
public static readonly int[] Legends =
{
@ -219,7 +222,7 @@
#region Games
public static readonly int[] Games_7vc2 = { 39, 40, 41 }; // Gold, Silver, Crystal
public static readonly int[] Games_7vc1 = { 35, 36, 37, 38 }; // Red, Blue, Green, Yellow
public static readonly int[] Games_7vc1 = { 35, 36, 37, 38 }; // Red, Green, Blue, Yellow
public static readonly int[] Games_7go = { 34 };
public static readonly int[] Games_7sm = { 30, 31 };
public static readonly int[] Games_6xy = { 24, 25 };

View file

@ -30,5 +30,129 @@ namespace PKHeX.Core
};
internal static readonly int[] TransferSpeciesDefaultAbility_1 = {92, 93, 94, 109, 110, 151};
internal static readonly int[] TMHM_RBY =
{
005, 013, 014, 018, 025, 092, 032, 034, 036, 038,
061, 055, 058, 059, 063, 006, 066, 068, 069, 099,
072, 076, 082, 085, 087, 089, 090, 091, 094, 100,
102, 104, 115, 117, 118, 120, 121, 126, 129, 130,
135, 138, 143, 156, 086, 149, 153, 157, 161, 164,
015, 019, 057, 070, 148
};
internal static readonly int[] WildPokeBalls1 = {4};
internal static readonly EncounterStatic[] Encounter_RBY =
{
new EncounterStatic { Species = 001, Level = 05, Version = GameVersion.RBY }, // Bulbasaur
new EncounterStatic { Species = 004, Level = 05, Version = GameVersion.RBY }, // Charmander
new EncounterStatic { Species = 007, Level = 05, Version = GameVersion.RBY }, // Squirtle
new EncounterStatic { Species = 025, Level = 05, Version = GameVersion.YW }, // Pikachu
// Game Corner
new EncounterStatic { Species = 030, Level = 17, Version = GameVersion.RBY }, // Nidorina (Red Game Corner)
new EncounterStatic { Species = 033, Level = 17, Version = GameVersion.RBY }, // Nidorino (Blue[EN] / Green[JP] Game Corner)
new EncounterStatic { Species = 035, Level = 08, Version = GameVersion.RBY }, // Clefairy (Red Game Corner)
new EncounterStatic { Species = 036, Level = 24, Version = GameVersion.RBY }, // Clefable (Blue[JP] Game Corner)
new EncounterStatic { Species = 037, Level = 18, Version = GameVersion.RBY }, // Vulpix (Yellow Game Corner)
new EncounterStatic { Species = 040, Level = 22, Version = GameVersion.RBY }, // Wigglytuff (Yellow Game Corner)
new EncounterStatic { Species = 063, Level = 06, Version = GameVersion.RBY }, // Abra (Blue[EN] / Green[JP] Game Corner)
new EncounterStatic { Species = 116, Level = 18, Version = GameVersion.RBY }, // Horsea (Blue[JP] Game Corner)
new EncounterStatic { Species = 123, Level = 25, Version = GameVersion.RBY }, // Scyther (Red Game Corner)
new EncounterStatic { Species = 127, Level = 20, Version = GameVersion.RBY }, // Pinsir (Blue[EN] / Green[JP] Game Corner)
new EncounterStatic { Species = 137, Level = 18, Version = GameVersion.RBY }, // Porygon (Blue[EN] / Green[JP] Game Corner)
new EncounterStatic { Species = 147, Level = 18, Version = GameVersion.RBY }, // Dratini (Red Game Corner)
new EncounterStatic { Species = 148, Level = 30, Version = GameVersion.RBY }, // Dragonair (Blue[JP] Game Corner)
// Lower level less ideal matches; best match is from above.
// new EncounterStatic { Species = 025, Level = 12 }, // Pikachu (Blue[JP] Game Corner)
// new EncounterStatic { Species = 035, Level = 12 }, // Clefairy (Blue[EN] / Green[JP] Game Corner)
// new EncounterStatic { Species = 063, Level = 09 }, // Abra (Red Game Corner)
// new EncounterStatic { Species = 063, Level = 08 }, // Abra (Blue[JP] Game Corner)
// new EncounterStatic { Species = 063, Level = 15 }, // Abra (Yellow Game Corner)
// new EncounterStatic { Species = 123, Level = 30 }, // Scyther (Yellow Game Corner)
// new EncounterStatic { Species = 127, Level = 30 }, // Pinsir (Yellow Game Corner)
// new EncounterStatic { Species = 137, Level = 22 }, // Porygon (Blue[JP] Game Corner)
// new EncounterStatic { Species = 137, Level = 26 }, // Porygon (Red Game Corner)
// new EncounterStatic { Species = 137, Level = 26 }, // Porygon (Yellow Game Corner)
// new EncounterStatic { Species = 147, Level = 24 }, // Dratini (Blue[EN] / Green[JP] Game Corner)
new EncounterStatic { Species = 129, Level = 05, Version = GameVersion.RBY }, // Magikarp
new EncounterStatic { Species = 143, Level = 30, Version = GameVersion.RBY }, // Snorlax
new EncounterStatic { Species = 106, Level = 30, Version = GameVersion.RBY }, // Hitmonlee
new EncounterStatic { Species = 107, Level = 30, Version = GameVersion.RBY }, // Hitmonchan
new EncounterStatic { Species = 131, Level = 15, Version = GameVersion.RBY }, // Lapras
new EncounterStatic { Species = 138, Level = 30, Version = GameVersion.RBY }, // Omanyte
new EncounterStatic { Species = 140, Level = 30, Version = GameVersion.RBY }, // Kabuto
new EncounterStatic { Species = 142, Level = 30, Version = GameVersion.RBY }, // Aerodactyl
new EncounterStatic { Species = 144, Level = 50, Version = GameVersion.RBY }, // Articuno
new EncounterStatic { Species = 145, Level = 50, Version = GameVersion.RBY }, // Zapdos
new EncounterStatic { Species = 146, Level = 50, Version = GameVersion.RBY }, // Moltres
new EncounterStatic { Species = 150, Level = 70, Version = GameVersion.RBY }, // Mewtwo
new EncounterStatic { Species = 133, Level = 25, Version = GameVersion.RBY }, // Eevee
// Yellow Only -- duplicate encounters with a higher level
// new EncounterStatic { Species = 133, Level = 25, Version = GameVersion.YW }, // Eevee (Celadon City)
// new EncounterStatic { Species = 001, Level = 10, Version = GameVersion.YW }, // Bulbasaur (Cerulean City)
// new EncounterStatic { Species = 004, Level = 10, Version = GameVersion.YW }, // Charmander (Route 24)
// new EncounterStatic { Species = 007, Level = 10, Version = GameVersion.YW }, // Squirtle (Vermillion City)
new EncounterStatic { Species = 054, Level = 15, Moves = new [] { 133, 10 }, Version = GameVersion.SPECIAL }, // Stadium Psyduck (Amnesia)
new EncounterStatic { Species = 151, Level = 5, IVs = new [] {15,15,15,15,15,15}, Version = GameVersion.SPECIAL }, // Event Mew
};
internal static readonly EncounterTrade[] TradeGift_RBY =
{
// Species & Minimum level (legal) possible to acquire at.
new EncounterTrade { Species = 122, Level = 06 }, // Mr. Mime - Game Corner Abra
new EncounterTrade { Species = 032, Level = 02 }, // Nidoran♂ - Wild Nidoran♀
new EncounterTrade { Species = 029, Level = 02 }, // Nidoran♀ - Wild Nidoran♂
new EncounterTrade { Species = 030, Level = 16 }, // Nidorina - Evolve Nidorino
new EncounterTrade { Species = 108, Level = 15 }, // Lickitung - Surf Slowbro
new EncounterTrade { Species = 124, Level = 15 }, // Jynx - Fish Poliwhirl (GSC: 10)
new EncounterTrade { Species = 083, Level = 02 }, // Farfetchd - Wild Spearow
new EncounterTrade { Species = 101, Level = 03 }, // Electrode - Wild Raichu
new EncounterTrade { Species = 114, Level = 13 }, // Tangela - Wild Venonat (GSC: 5)
new EncounterTrade { Species = 086, Level = 28 }, // Seel - Wild Ponyta (GSC: 6)
//new EncounterTrade { Species = 122, Level = 03 }, // Mr. Mime - Wild Jigglypuff
new EncounterTrade { Species = 060, Level = 02 }, // Poliwag - Wild Rattata
new EncounterTrade { Species = 115, Level = 42 }, // Kangaskhan - Evolve Rhydon (GSC: 30)
new EncounterTrade { Species = 128, Level = 28 }, // Tauros - Evolve Persian (GSC: 18)
new EncounterTrade { Species = 093, Level = 20 }, // Haunter - Trade Machoke (GSC: 10)
//new EncounterTrade { Species = 083, Level = 02 }, // Farfetchd - Wild Pidgey
new EncounterTrade { Species = 075, Level = 16 }, // Graveler - Evolve Kadabra (GSC: 15)
new EncounterTrade { Species = 079, Level = 22 }, // Slowpoke - Wild Seel
new EncounterTrade { Species = 098, Level = 15 }, // Krabby - Wild Growlithe (GSC: 5)
//new EncounterTrade { Species = 122, Level = 08 }, // Mr. Mime - Wild Clefairy (GSC: 6)
new EncounterTrade { Species = 067, Level = 20 }, // Machoke - Wild Cubone (GSC: 10)
new EncounterTrade { Species = 051, Level = 15 }, // Dugtrio - Trade Lickitung
new EncounterTrade { Species = 047, Level = 13 }, // Parasect - Trade Tangela
new EncounterTrade { Species = 112, Level = 15 }, // Rhydon - Surf Golduck (GSC: 10)
new EncounterTrade { Species = 087, Level = 15 }, // Dewgong - Wild Growlithe (GSC: 5)
new EncounterTrade { Species = 089, Level = 25 }, // Muk - Wild Kangaskhan (GSC: 14)
};
internal static readonly EncounterArea FishOldGood_RBY = new EncounterArea { Location = -1, Slots = new EncounterSlot[]
{
new EncounterSlot1 {Species = 129, LevelMin = 05, LevelMax = 05, Type = SlotType.Old_Rod, Rate = -1, }, // Magikarp
new EncounterSlot1 {Species = 118, LevelMin = 10, LevelMax = 10, Type = SlotType.Good_Rod, Rate = -1, }, // Goldeen
new EncounterSlot1 {Species = 060, LevelMin = 10, LevelMax = 10, Type = SlotType.Good_Rod, Rate = -1, }, // Poliwag
}};
internal static readonly int[] FutureEvolutionsGen1 =
{
169,182,186,196,197,199,208,212,230,233,242,462,463,464,465,466,467,470,471,474,700
};
internal static readonly int[] FutureEvolutionsGen1_Gen2LevelUp =
{
169,196,197,242
};
//Crobat Espeon Umbreon Blissey
}
}

View file

@ -13,7 +13,7 @@ namespace PKHeX.Core
3, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 51, 52, 53, 57, 60, 62, 63, 64, 65, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, 96, 97, 98, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 117, 118, 119, 121, 122, 123, 124, 125, 126, 131, 132, 138, 139, 140, 143, 144, 146, 150, 151, 152, 156, 158, 163, 168, 169, 170, 172, 173, 174, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189
};
internal static readonly ushort[] Pouch_Ball_GSC = {
1, 2, 4, 5, 157, 159, 160, 161, 164, 165, 166, 167
1, 2, 4, 5, 157, 159, 160, 161, 164, 165, 166
};
internal static readonly ushort[] Pouch_Key_GS = {
7, 54, 55, 58, 59, 61, 66, 67, 68 , 69, 71, 127, 128, 130, 133, 134, 175, 178
@ -35,5 +35,15 @@ namespace PKHeX.Core
10, 05, 10, 20, 20, 40, 15, 10, 20, 20, 25, 05, 15, 10, 05, 20, 15, 20, 25, 20, 05, 30, 05, 10, 20, 40, 05, 20, 40, 20, 15, 35, 10, 05, 05, 05, 15, 05, 20, 05, 05, 15, 20, 10, 05, 05, 15, 15, 15, 15,
10, 00, 00, 00, 00
};
internal static readonly int[] WildPokeBalls2 = { 4 };
internal static readonly int[] FutureEvolutionsGen2 =
{
424,429,430,461,462,463,464,465,466,467,468,469,470,471,472,473,474,700
};
internal static readonly EncounterTrade[] TradeGift_GSC =
{
};
}
}

View file

@ -81,5 +81,18 @@ namespace PKHeX.Core
580, 581, 582, 583, 584, 585, 586, 587, 588, 589,
590, 591, 592, 593
};
internal static readonly int[] WildPokeBalls3 = {1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12};
internal static readonly int[] FutureEvolutionsGen3 =
{
407,424,429,430,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,700
};
internal static readonly int[] FutureEvolutionsGen3_LevelUp =
{
424, 461, 462, 463, 465, 469, 470, 471, 472, 473, 476
};
// Ambipom Weavile Magnezone Lickilicky Tangrowth
// Yanmega Leafeon Glaceon Mamoswine Gliscor Probopass
}
}

View file

@ -118,5 +118,22 @@ namespace PKHeX.Core
10, 15, 20, 15, 10, 10, 10, 20, 05, 30, 05, 10, 15, 10, 10, 05, 20, 30, 10, 30, 15, 15, 15, 15, 30, 10, 20, 15, 10, 10, 20, 15, 05, 05, 15, 15, 05, 10, 05, 20, 05, 15, 20, 05, 20, 20, 20, 20, 10, 20,
10, 15, 20, 15, 10, 10, 05, 10, 05, 05, 10, 05, 05, 10, 05, 05, 05,
};
internal static readonly int[] WildPokeBalls4_DPPt =
{
1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
// Cherish ball not usable
};
internal static readonly int[] WildPokeBalls4_HGSS =
{
1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
// Cherish ball not usable
17, 18, 19, 20, 21, 22,
// Comp Ball not usable in wild
};
internal static readonly int[] FutureEvolutionsGen4 =
{
700
};
}
}

View file

@ -82,5 +82,17 @@ namespace PKHeX.Core
15, 15, 15, 15, 10, 10, 10, 10, 10, 15, 15, 15, 15, 05, 05, 15, 05, 10, 10, 10, 20, 20, 20, 10, 10, 30, 15, 15, 10, 15, 25, 10, 20, 10, 10, 10, 20, 10, 10, 10, 10, 10, 15, 15, 05, 05, 10, 10, 10, 05,
05, 10, 05, 05, 15, 10, 05, 05, 05,
};
internal static readonly int[] WildPokeBalls5 =
{
1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
// Cherish ball not usable
// HGSS balls not usable
// Dream ball not usable in wild
};
internal static readonly int[] FutureEvolutionsGen5 =
{
700
};
}
}

View file

@ -225,17 +225,7 @@ namespace PKHeX.Core
};
#endregion
// Legality
internal const int Struggle = 165;
internal const int Chatter = 448;
internal static readonly int[] InvalidSketch =
{
// Regular Moves
Struggle, Chatter
// Z-Moves
// TODO
};
internal static readonly int[] EggLocations = {60002, 30002};
internal static readonly int[] ValidMet_XY =
{
@ -633,6 +623,9 @@ namespace PKHeX.Core
577, // Solosis
582, // Vanillite
595, // Joltik
// Via Incense Breeding
298, 360, 406, 433, 438, 439, 440, 446, 458,
};
internal static readonly int[] Ban_DreamHidden =
{

View file

@ -470,17 +470,9 @@ namespace PKHeX.Core
599, // Klink
622, // Golett
};
internal static readonly int[] Ban_Gen3Ball_AllowG7 =
internal static readonly int[] Ban_Gen3Ball_7 =
{
252, 255, 258, //1 - Treeko, Torchic, Mudkip
253, 256, 259, //2
254, 257, 260, //3
387, 390, 393, //1 - Turtwig, Chimchar, Piplup
388, 391, 394, //2
389, 392, 395, //3
495, 498, 501, //1 - Snivy, Tepig, Oshawott
496, 499, 502, //2
497, 500, 503, //3
566, 567, 696, 697, 698, 699 // Fossil Only obtain
};
internal static readonly int[] Ban_Gen4Ball_7 =
{
@ -1173,7 +1165,7 @@ namespace PKHeX.Core
new EncounterStatic { Species = 422, Relearn = new[] {124}}, // Shellos: Sludge
new EncounterStatic { Species = 131, Relearn = new[] {419}}, // Lapras: Avalanche
new EncounterStatic { Species = 102, Relearn = new[] {335,285,384,381,267,246}},// Exeggcute: Block, Skill Swap, Power Swap, Lucky Chant, Nature Power, Ancient Power
new EncounterStatic { Species = 123, Relearn = new[] {211,501,432,179,68}}, // Scyther: Steel Wing, Quick Guard, Defog, Reversal, Counter
new EncounterStatic { Species = 123, Relearn = new[] {211,501,432,179,68}}, // Scyther: Steel Wing, Quick Guard, Defog, Reversal, Counter
new EncounterStatic { Species = 198, Relearn = new[] {375,260,195}}, // Murkrow: Psycho Shift, Flatter, Perish Song
new EncounterStatic { Species = 447, Relearn = new[] {299}}, // Riolu: Blaze Kick
new EncounterStatic { Species = 147, Relearn = new[] {245}}, // Dratini: Extreme Speed

View file

@ -21,31 +21,31 @@ namespace PKHeX.Core
public bool PL_enabled { get { return PL_Flag != 0; } set { PL_Flag = (byte)(value ? 1 << 7 : 0); } }
//Name of data source
public string Origin_app {
public string Origin_app {
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x01, 0x6E)); }
set { Encoding.Unicode.GetBytes(value.PadRight(54 + 1, '\0')).CopyTo(Data, 0x01); } }
//Pokemon transfer flags?
public uint PKM1_flags {
get { return BitConverter.ToUInt32(Data, 0x99); }
get { return BitConverter.ToUInt32(Data, 0x99); }
set { BitConverter.GetBytes(value).CopyTo(Data, 0x99); } }
public uint PKM2_flags {
get { return BitConverter.ToUInt32(Data, 0x141); }
get { return BitConverter.ToUInt32(Data, 0x141); }
set { BitConverter.GetBytes(value).CopyTo(Data, 0x141); } }
public uint PKM3_flags {
get { return BitConverter.ToUInt32(Data, 0x1E9); }
get { return BitConverter.ToUInt32(Data, 0x1E9); }
set { BitConverter.GetBytes(value).CopyTo(Data, 0x1E9); } }
public uint PKM4_flags {
get { return BitConverter.ToUInt32(Data, 0x291); }
get { return BitConverter.ToUInt32(Data, 0x291); }
set { BitConverter.GetBytes(value).CopyTo(Data, 0x291); } }
public uint PKM5_flags {
get { return BitConverter.ToUInt32(Data, 0x339); }
get { return BitConverter.ToUInt32(Data, 0x339); }
set { BitConverter.GetBytes(value).CopyTo(Data, 0x339); } }
public uint PKM6_flags {
get { return BitConverter.ToUInt32(Data, 0x3E1); }
get { return BitConverter.ToUInt32(Data, 0x3E1); }
set { BitConverter.GetBytes(value).CopyTo(Data, 0x3E1); } }
public uint[] Flags
public uint[] Flags
{
get { return new[] {PKM1_flags, PKM2_flags, PKM3_flags, PKM4_flags, PKM5_flags, PKM6_flags}; }
set
@ -58,29 +58,29 @@ namespace PKHeX.Core
if (value.Length > 5) PKM6_flags = value[5];
}
}
//Pokémon
public PL6_PKM poke1 {
get { return new PL6_PKM(Data.Skip(0x9D).Take(PL6_PKM.Size).ToArray()); }
//Pokémon
public PL6_PKM poke1 {
get { return new PL6_PKM(Data.Skip(0x9D).Take(PL6_PKM.Size).ToArray()); }
set { value.Data.CopyTo(Data, 0x9D); } }
public PL6_PKM poke2 {
get { return new PL6_PKM(Data.Skip(0x145).Take(PL6_PKM.Size).ToArray()); }
public PL6_PKM poke2 {
get { return new PL6_PKM(Data.Skip(0x145).Take(PL6_PKM.Size).ToArray()); }
set { value.Data.CopyTo(Data, 0x145); } }
public PL6_PKM poke3 {
get { return new PL6_PKM(Data.Skip(0x1ED).Take(PL6_PKM.Size).ToArray()); }
public PL6_PKM poke3 {
get { return new PL6_PKM(Data.Skip(0x1ED).Take(PL6_PKM.Size).ToArray()); }
set { value.Data.CopyTo(Data, 0x1ED); } }
public PL6_PKM poke4 {
get { return new PL6_PKM(Data.Skip(0x295).Take(PL6_PKM.Size).ToArray()); }
public PL6_PKM poke4 {
get { return new PL6_PKM(Data.Skip(0x295).Take(PL6_PKM.Size).ToArray()); }
set { value.Data.CopyTo(Data, 0x295); } }
public PL6_PKM poke5 {
get { return new PL6_PKM(Data.Skip(0x33D).Take(PL6_PKM.Size).ToArray()); }
public PL6_PKM poke5 {
get { return new PL6_PKM(Data.Skip(0x33D).Take(PL6_PKM.Size).ToArray()); }
set { value.Data.CopyTo(Data, 0x33D); } }
public PL6_PKM poke6 {
get { return new PL6_PKM(Data.Skip(0x3E5).Take(PL6_PKM.Size).ToArray()); }
public PL6_PKM poke6 {
get { return new PL6_PKM(Data.Skip(0x3E5).Take(PL6_PKM.Size).ToArray()); }
set { value.Data.CopyTo(Data, 0x3E5); } }
public PL6_PKM[] Pokes
public PL6_PKM[] Pokes
{
get { return new[] {poke1, poke2, poke3, poke4, poke5, poke6}; }
set
@ -132,7 +132,7 @@ namespace PKHeX.Core
get { return BitConverter.ToUInt16(Data, 0x49F); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x49F); } }
public int[] Items
public int[] Items
{
get { return new[] {Item_1, Item_2, Item_3, Item_4, Item_5, Item_6}; }
set
@ -145,8 +145,8 @@ namespace PKHeX.Core
if (value.Length > 5) Item_6 = value[5];
}
}
public int[] Quantities
public int[] Quantities
{
get { return new[] {Quantity_1, Quantity_2, Quantity_3, Quantity_4, Quantity_5, Quantity_6}; }
set
@ -169,7 +169,7 @@ namespace PKHeX.Core
public int Pokemiles {
get { return BitConverter.ToUInt16(Data, 0x4A3); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4A3); } }
}
}
public class PL6_PKM : IEncounterable
{
@ -242,7 +242,7 @@ namespace PKHeX.Core
public int MetLocation {
get { return BitConverter.ToUInt16(Data, 0x3E); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3F); } }
public int MetLevel {
public int MetLevel {
get { return Data[0x40]; }
set { Data[0x40] = (byte)value; } }

View file

@ -278,6 +278,7 @@ namespace PKHeX.Core
return null;
int currentLevel = Level > 0 ? Level : (int)(Util.rnd32()%100 + 1);
var pi = PersonalTable.AO[Species];
PK6 pk = new PK6
{
Species = Species,
@ -286,11 +287,11 @@ namespace PKHeX.Core
SID = SID,
Met_Level = currentLevel,
Nature = Nature != 0xFF ? Nature : (int)(Util.rnd32() % 25),
Gender = PersonalTable.AO[Species].Gender == 255 ? 2 : (Gender != 3 ? Gender : PersonalTable.AO[Species].RandomGender),
Gender = Gender != 3 ? Gender : pi.RandomGender,
AltForm = Form,
EncryptionConstant = EncryptionConstant == 0 ? Util.rnd32() : EncryptionConstant,
Version = OriginGame == 0 ? SAV.Game : OriginGame,
Language = Language == 0 ? SAV.Language : Language,
EncryptionConstant = EncryptionConstant != 0 ? EncryptionConstant : Util.rnd32(),
Version = OriginGame != 0 ? OriginGame : SAV.Game,
Language = Language != 0 ? Language : SAV.Language,
Ball = Ball,
Country = SAV.Country,
Region = SAV.SubRegion,
@ -334,7 +335,7 @@ namespace PKHeX.Core
RibbonChampionNational = RibbonChampionNational,
RibbonChampionWorld = RibbonChampionWorld,
OT_Friendship = PersonalTable.AO[Species].BaseFriendship,
OT_Friendship = pi.BaseFriendship,
OT_Intensity = OT_Intensity,
OT_Memory = OT_Memory,
OT_TextVar = OT_TextVar,

View file

@ -108,10 +108,32 @@ namespace PKHeX.Core
public override int Item {
get { return BitConverter.ToUInt16(Data, 0x68); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x68); } }
public int GetItem(int index)
{
return BitConverter.ToUInt16(Data, 0x68 + 0x4*index);
}
public void SetItem(int index, ushort item)
{
BitConverter.GetBytes(item).CopyTo(Data, 0x68 + 4*index);
}
public override int Quantity {
get { return BitConverter.ToUInt16(Data, 0x6A); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x6A); } }
public int GetQuantity(int index)
{
return BitConverter.ToUInt16(Data, 0x6A + 0x4 * index);
}
public void SetQuantity(int index, ushort quantity)
{
BitConverter.GetBytes(quantity).CopyTo(Data, 0x6A + 4 * index);
}
// Pokémon Properties
public override bool IsPokémon { get { return CardType == 0; } set { if (value) CardType = 0; } }
public override bool IsShiny => PIDType == 2;
@ -297,6 +319,7 @@ namespace PKHeX.Core
int currentLevel = Level > 0 ? Level : (int)(Util.rnd32()%100 + 1);
int metLevel = MetLevel > 0 ? MetLevel : currentLevel;
var pi = PersonalTable.SM[Species];
PK7 pk = new PK7
{
Species = Species,
@ -305,11 +328,11 @@ namespace PKHeX.Core
SID = SID,
Met_Level = metLevel,
Nature = Nature != 0xFF ? Nature : (int)(Util.rnd32() % 25),
Gender = PersonalTable.AO[Species].Gender == 255 ? 2 : (Gender != 3 ? Gender : PersonalTable.AO[Species].RandomGender),
Gender = Gender != 3 ? Gender : pi.RandomGender,
AltForm = Form,
EncryptionConstant = EncryptionConstant == 0 ? Util.rnd32() : EncryptionConstant,
Version = OriginGame == 0 ? SAV.Game : OriginGame,
Language = Language == 0 ? SAV.Language : Language,
EncryptionConstant = EncryptionConstant != 0 ? EncryptionConstant : Util.rnd32(),
Version = OriginGame != 0 ? OriginGame : SAV.Game,
Language = Language != 0 ? Language : SAV.Language,
Ball = Ball,
Country = SAV.Country,
Region = SAV.SubRegion,
@ -353,7 +376,7 @@ namespace PKHeX.Core
RibbonChampionNational = RibbonChampionNational,
RibbonChampionWorld = RibbonChampionWorld,
OT_Friendship = PersonalTable.AO[Species].BaseFriendship,
OT_Friendship = pi.BaseFriendship,
OT_Intensity = OT_Intensity,
OT_Memory = OT_Memory,
OT_TextVar = OT_TextVar,

View file

@ -2,7 +2,7 @@
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{B4EFF030-C75A-49F9-A4BC-738D1B61C4AF}</ProjectGuid>
@ -261,17 +261,31 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<None Include="Resources\byte\eggmove_sm.pkl" />
<None Include="Resources\byte\encounter_blue.pkl" />
<None Include="Resources\byte\encounter_mn.pkl" />
<None Include="Resources\byte\encounter_mn_sos.pkl" />
<None Include="Resources\byte\encounter_rb_f.pkl" />
<None Include="Resources\byte\encounter_red.pkl" />
<None Include="Resources\byte\encounter_sn.pkl" />
<None Include="Resources\byte\encounter_sn_sos.pkl" />
<None Include="Resources\byte\encounter_yellow.pkl" />
<None Include="Resources\byte\encounter_yellow_f.pkl" />
<None Include="Resources\byte\evos_gsc.pkl" />
<None Include="Resources\byte\evos_rby.pkl" />
<None Include="Resources\byte\evos_sm.pkl" />
<None Include="Resources\byte\fashion_f_sm" />
<None Include="Resources\byte\fashion_f_sm_illegal" />
<None Include="Resources\byte\fashion_m_sm" />
<None Include="Resources\byte\fashion_m_sm_illegal" />
<None Include="Resources\byte\lvlmove_c.pkl" />
<None Include="Resources\byte\lvlmove_gs.pkl" />
<None Include="Resources\byte\lvlmove_rb.pkl" />
<None Include="Resources\byte\lvlmove_rby.pkl" />
<None Include="Resources\byte\lvlmove_sm.pkl" />
<None Include="Resources\byte\lvlmove_y.pkl" />
<None Include="Resources\byte\personal_rb" />
<None Include="Resources\byte\personal_sm" />
<None Include="Resources\byte\personal_y" />
<None Include="Resources\byte\wc7.pkl" />
<None Include="Resources\byte\wc7full.pkl" />
<None Include="Resources\text\gen3\text_gsc_00000_es.txt" />

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core
// Internal use only
protected internal byte[] otname;
protected internal byte[] nick;
public override PersonalInfo PersonalInfo => PersonalTable.RBY[Species];
public override PersonalInfo PersonalInfo => PersonalTable.Y[Species];
public byte[] OT_Name_Raw => (byte[])otname.Clone();
public byte[] Nickname_Raw => (byte[])nick.Clone();
@ -259,7 +259,7 @@ namespace PKHeX.Core
public override int OT_Friendship { get { return 0; } set { } }
public override int OT_Gender { get { return 0; } set { } }
public override int Ball { get { return 0; } set { } }
public override int Version { get { return 0; } set { } }
public override int Version { get { return (int)GameVersion.RBY; } set { } }
public override int SID { get { return 0; } set { } }
public override int PKRS_Strain { get { return 0; } set { } }
public override int PKRS_Days { get { return 0; } set { } }
@ -353,6 +353,11 @@ namespace PKHeX.Core
Geo1_Region = PKMConverter.Region
};
pk7.Nickname = PKX.getSpeciesNameGeneration(pk7.Species, pk7.Language, pk7.Format);
if (otname[0] == 0x5D) // Ingame Trade
{
var s = PKX.getG1Char(0x5D, Japanese);
pk7.OT_Name = s.Substring(0, 1) + s.Substring(1).ToLower();
}
pk7.OT_Friendship = pk7.HT_Friendship = PersonalTable.SM[Species].BaseFriendship;
// IVs
@ -381,6 +386,18 @@ namespace PKHeX.Core
}
pk7.TradeMemory(Bank:true); // oh no, memories on gen7 pkm
if (pk7.Species == 150) // Pay Day Mewtwo
{
var moves = pk7.Moves;
var index = Array.IndexOf(moves, 6);
if (index != -1)
{
moves[index] = 0;
pk7.Moves = moves;
pk7.FixMoves();
}
}
pk7.RefreshChecksum();
return pk7;

View file

@ -306,7 +306,7 @@ namespace PKHeX.Core
public override int HPType
{
get { return 4 * IV_ATK % 4 + IV_DEF % 4; }
get { return ((IV_ATK & 3) << 2) | (IV_DEF & 3); }
set
{
@ -327,7 +327,7 @@ namespace PKHeX.Core
public override int Egg_Location { get { return 0; } set { } }
public override int OT_Friendship { get { return 0; } set { } }
public override int Ball { get { return 0; } set { } }
public override int Version { get { return 0; } set { } }
public override int Version { get { return (int)GameVersion.GSC; } set { } }
public override int SID { get { return 0; } set { } }
public override int CNT_Cool { get { return 0; } set { } }
public override int CNT_Beauty { get { return 0; } set { } }

View file

@ -268,24 +268,28 @@ namespace PKHeX.Core
public bool AO => Version == (int)GameVersion.AS || Version == (int)GameVersion.OR;
public bool SM => Version == (int)GameVersion.SN || Version == (int)GameVersion.MN;
protected bool PtHGSS => GameVersion.Pt == (GameVersion)Version || HGSS;
protected bool HGSS => new[] {GameVersion.HG, GameVersion.SS}.Contains((GameVersion)Version);
public bool HGSS => new[] {GameVersion.HG, GameVersion.SS}.Contains((GameVersion)Version);
public bool VC => VC1 || VC2;
public bool Gen7 => Version >= 30 && Version <= 33;
public bool Gen6 => Version >= 24 && Version <= 29;
public bool Gen5 => Version >= 20 && Version <= 23;
public bool Gen4 => Version >= 7 && Version <= 12 && Version != 9;
public bool Gen3 => Version >= 1 && Version <= 5 || Version == 15;
public bool GenU => !(Gen7 || Gen6 || Gen5 || Gen4 || Gen3);
public bool Gen2 => Version == (int)GameVersion.GSC;
public bool Gen1 => Version == (int)GameVersion.RBY;
public bool GenU => !(Gen7 || Gen6 || Gen5 || Gen4 || Gen3 || Gen2 || Gen1 || VC);
public int GenNumber
{
get
{
if (VC) return 7;
if (Gen7) return 7;
if (Gen6) return 6;
if (Gen5) return 5;
if (Gen4) return 4;
if (Gen3) return 3;
if (Gen2) return Format; // 2
if (Gen1) return Format; // 1
if (VC) return 1;
return -1;
}
}
@ -399,28 +403,17 @@ namespace PKHeX.Core
}
// Legality Extensions
private int MaxSpeciesID => Legal.getMaxSpeciesOrigin(Format);
public virtual bool WasLink => false;
public virtual bool WasEgg => Egg_Location > 0;
public virtual bool WasEvent => Met_Location > 40000 && Met_Location < 50000 || FatefulEncounter;
public virtual bool WasEventEgg => ((Egg_Location > 40000 && Egg_Location < 50000) || (FatefulEncounter && Egg_Location > 0)) && Met_Level == 1;
public virtual bool WasTradedEgg => Egg_Location == 30002;
public virtual bool WasIngameTrade => Met_Location == 30001;
public virtual bool IsUntraded => string.IsNullOrWhiteSpace(HT_Name) && GenNumber == Format;
public virtual bool IsUntraded => Format >= 6 && string.IsNullOrWhiteSpace(HT_Name) && GenNumber == Format;
public virtual bool IsNative => GenNumber == Format;
public virtual bool IsOriginValid()
{
switch (GenNumber)
{
case 1: return Species <= Legal.MaxSpeciesID_1;
case 2: return Species <= Legal.MaxSpeciesID_2;
case 3: return Species <= Legal.MaxSpeciesID_3;
case 4: return Species <= Legal.MaxSpeciesID_4;
case 5: return Species <= Legal.MaxSpeciesID_5;
case 6: return Species <= Legal.MaxSpeciesID_6;
case 7: return Species <= Legal.MaxSpeciesID_7;
default: return false;
}
}
public virtual bool IsOriginValid => Species <= Legal.getMaxSpeciesOrigin(Format);
public virtual bool SecretSuperTrainingUnlocked { get { return false; } set { } }
public virtual bool SecretSuperTrainingComplete { get { return false; } set { } }
@ -453,31 +446,47 @@ namespace PKHeX.Core
/// Checks if the <see cref="PKM"/> could inhabit a set of games.
/// </summary>
/// <param name="Generation">Set of games.</param>
/// <param name="species"></param>
/// <returns>True if could inhabit, False if not.</returns>
public bool InhabitedGeneration(int Generation)
public bool InhabitedGeneration(int Generation, int species = -1)
{
if (VC1 && Generation == 1)
return true; // Virtual Console Gen1
if (species < 0)
species = Species;
if (Format == Generation)
return true;
if (Format < Generation)
return false; // Future
if (GenNumber > Generation)
return false; // Past
switch (Generation) // Sanity Check Species ID
if (!IsOriginValid)
return false;
// Sanity Check Species ID
if (Legal.getMaxSpeciesOrigin(GenNumber) < species && !Legal.getFutureGenEvolutions(GenNumber).Contains(species))
return false;
int gen = GenNumber;
switch (Generation)
{
case 1: return Species <= Legal.MaxSpeciesID_1;
case 2: return Species <= Legal.MaxSpeciesID_2;
case 3: return Species <= Legal.MaxSpeciesID_3;
case 4: return Species <= Legal.MaxSpeciesID_4;
case 5: return Species <= Legal.MaxSpeciesID_5;
case 6: return Species <= Legal.MaxSpeciesID_6;
case 7: return Species <= Legal.MaxSpeciesID_7;
case 1:
case 2: return Format <= 2 || VC;
case 3: return Gen3;
case 4: return 3 <= gen && gen <= 4;
case 5: return 3 <= gen && gen <= 5;
case 6: return 3 <= gen && gen <= 6;
case 7: return VC || 3 <= gen && gen <= 7;
default:
return false;
}
}
/// <summary>
/// Checks if the PKM has its original met location.
/// </summary>
/// <returns>Returns false if the Met Location has been overwritten via generational transfer.</returns>
public bool HasOriginalMetLocation => !(Format < 3 || VC || GenNumber <= 4 && Format != GenNumber);
/// <summary>
/// Checks if the current <see cref="Gender"/> is valid.
/// </summary>

View file

@ -2017,7 +2017,7 @@ namespace PKHeX.Core
#region Gen 1 Character Tables
private static Dictionary<byte, string> RBY2U_U => new Dictionary<byte, string>{
{0x50, "\0"},
{0x5D, "[TRAINER]"},
{0x5D, "TRAINER"},
{0x7F, " "},
{0x80, "A"},
{0x81, "B"},
@ -2103,7 +2103,7 @@ namespace PKHeX.Core
private static Dictionary<string, byte> U2RBY_U => new Dictionary<string, byte> {
{"\0", 0x50},
{"[TRAINER]", 0x5D},
{"TRAINER", 0x5D},
{" ", 0x7F},
{"A", 0x80},
{"B", 0x81},
@ -2562,6 +2562,18 @@ namespace PKHeX.Core
.Aggregate("", (current, cur) => current + cur);
}
/// <summary>
/// Converts Generation 1 encoded character to string.
/// </summary>
/// <param name="key">Encoded character.</param>
/// <param name="jp">Data source is Japanese.</param>
/// <returns>Decoded string.</returns>
public static string getG1Char(byte key, bool jp)
{
Dictionary<byte, string> dict = jp ? RBY2U_J : RBY2U_U;
return dict.ContainsKey(key) ? dict[key] : "";
}
/// <summary>
/// Converts Generation 1 encoded data the same way Bank converts.
/// </summary>
@ -2573,7 +2585,7 @@ namespace PKHeX.Core
var us_table = new ushort[] { 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x0028, 0x0029, 0x003A, 0x003B, 0x0028, 0x0029, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x00C4, 0x00D6, 0x00DC, 0x00E4, 0x00F6, 0x00FC, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0050, 0x004D, 0x002D, 0x0020, 0x0020, 0x003F, 0x0021, 0x002D, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0xE08E, 0x0020, 0x0078, 0x002E, 0x002F, 0x002C, 0xE08F, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 };
var jp_table = new ushort[] { 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30AC, 0x30AE, 0x30B0, 0x30B2, 0x30B4, 0x30B6, 0x30B8, 0x30BA, 0x30BC, 0x30BE, 0x30C0, 0x30C2, 0x30C5, 0x30C7, 0x30C9, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30D0, 0x30D3, 0x30D6, 0x30DC, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x304C, 0x304E, 0x3050, 0x3052, 0x3054, 0x3056, 0x3058, 0x305A, 0x305C, 0x305E, 0x3060, 0x3062, 0x3065, 0x3067, 0x3069, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3070, 0x3073, 0x3076, 0x30D9, 0x307C, 0x3000, 0x30D1, 0x30D4, 0x30D7, 0x30DD, 0x3071, 0x3074, 0x3077, 0x30DA, 0x307D, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x0000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30DB, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F2, 0x30F3, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30A3, 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3072, 0x3075, 0x30D8, 0x307B, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x30EA, 0x308B, 0x308C, 0x308D, 0x308F, 0x3092, 0x3093, 0x3063, 0x3083, 0x3085, 0x3087, 0x30FC, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x30A1, 0x30A5, 0x30A7, 0x3000, 0x3000, 0x3000, 0x2642, 0x3000, 0x3000, 0x3000, 0x3000, 0x30A9, 0x2640, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000, 0x3000 };
var table = jp ? jp_table : us_table;
return Util.TrimFromZero(new string(strdata.Select(b => (char)table[b]).ToArray()));
return Util.TrimFromZero(new string(strdata.TakeWhile(b => b != 0).Select(b => (char)table[b]).ToArray()));
}
/// <summary>
@ -2640,43 +2652,44 @@ namespace PKHeX.Core
// Extensions
public static string getLocation(this PKM pk, bool eggmet)
{
if (pk.Format <= 2)
if (pk.Format < 2)
return "";
int gen = -1;
int bankID = 0;
int locval = eggmet ? pk.Egg_Location : pk.Met_Location;
if (pk.Format == 2)
return GameInfo.Strings.metGSC_00000[locval];
if (pk.Format == 3)
return GameInfo.Strings.metRSEFRLG_00000[locval % 0x100];
if (pk.Gen4 && (eggmet || pk.Format == 4))
gen = 2;
else if (pk.Format == 3)
gen = 3;
else if (pk.Gen4 && (eggmet || pk.Format == 4)) // 4
{
if (locval < 2000) return GameInfo.Strings.metHGSS_00000[locval];
if (locval < 3000) return GameInfo.Strings.metHGSS_02000[locval % 2000];
return GameInfo.Strings.metHGSS_03000[locval % 3000];
const int size = 1000;
bankID = locval/size;
gen = 4;
locval %= size;
}
if (pk.Gen5 || pk.Format <= 5)
else // 5-7+
{
if (locval < 30000) return GameInfo.Strings.metBW2_00000[locval];
if (locval < 40000) return GameInfo.Strings.metBW2_30000[locval % 10000 - 1];
if (locval < 60000) return GameInfo.Strings.metBW2_40000[locval % 10000 - 1];
return GameInfo.Strings.metBW2_60000[locval % 10000 - 1];
const int size = 10000;
bankID = locval/size;
int g = pk.GenNumber;
if (g >= 5)
gen = g;
else if (pk.Format >= 5)
gen = pk.Format;
locval %= size;
if (bankID >= 3)
locval -= 1;
}
if (pk.Gen6 || pk.Format <= 6)
{
if (locval < 30000) return GameInfo.Strings.metXY_00000[locval];
if (locval < 40000) return GameInfo.Strings.metXY_30000[locval % 10000 - 1];
if (locval < 60000) return GameInfo.Strings.metXY_40000[locval % 10000 - 1];
return GameInfo.Strings.metXY_60000[locval % 10000 - 1];
}
if (pk.Gen7 || pk.Format <= 7)
{
if (locval < 30000) return GameInfo.Strings.metSM_00000[locval];
if (locval < 40000) return GameInfo.Strings.metSM_30000[locval % 10000 - 1];
if (locval < 60000) return GameInfo.Strings.metSM_40000[locval % 10000 - 1];
return GameInfo.Strings.metSM_60000[locval % 10000 - 1];
}
return null; // Shouldn't happen for gen 3+
var bank = GameInfo.getLocationNames(gen, bankID);
if (bank == null || bank.Length <= locval)
return "";
return bank[locval];
}
public static string[] getQRText(this PKM pkm)
{

View file

@ -40,10 +40,10 @@ namespace PKHeX.Core
}
public override int CatchRate { get { return Data[0x08]; } set { Data[0x08] = (byte)value; } }
public override int BaseEXP { get { return Data[0x09]; } set { Data[0x09] = (byte)value; } }
public int Move1 { get { return Data[0x0A]; } set { Data[0x0A] = (byte)value; } }
public int Move2 { get { return Data[0x0B]; } set { Data[0x0B] = (byte)value; } }
public int Move3 { get { return Data[0x0C]; } set { Data[0x0C] = (byte)value; } }
public int Move4 { get { return Data[0x0D]; } set { Data[0x0D] = (byte)value; } }
public int Move1 { get { return Data[0x0F]; } set { Data[0x0F] = (byte)value; } }
public int Move2 { get { return Data[0x10]; } set { Data[0x10] = (byte)value; } }
public int Move3 { get { return Data[0x11]; } set { Data[0x11] = (byte)value; } }
public int Move4 { get { return Data[0x12]; } set { Data[0x12] = (byte)value; } }
public override int EXPGrowth { get { return Data[0x13]; } set { Data[0x13] = (byte)value; } }
// EV Yields are just aliases for base stats in Gen I
@ -64,5 +64,11 @@ namespace PKHeX.Core
public override int BaseFriendship { get { return 0; } set { } }
public override int EscapeRate { get { return 0; } set { } }
public override int Color { get { return 0; } set { } }
public int[] Moves
{
get { return new[] { Move1, Move2, Move3, Move4 }; }
set { if (value?.Length != 4) return; Move1 = value[0]; Move2 = value[1]; Move3 = value[2]; Move4 = value[3]; }
}
}
}

View file

@ -18,7 +18,8 @@ namespace PKHeX.Core
public static readonly PersonalTable RS = new PersonalTable(Properties.Resources.personal_rs, GameVersion.RS);
public static readonly PersonalTable C = new PersonalTable(Properties.Resources.personal_c, GameVersion.C);
public static readonly PersonalTable GS = new PersonalTable(Properties.Resources.personal_c, GameVersion.GS);
public static readonly PersonalTable RBY = new PersonalTable(Properties.Resources.personal_rby, GameVersion.RBY);
public static readonly PersonalTable RB = new PersonalTable(Properties.Resources.personal_rb, GameVersion.RBY);
public static readonly PersonalTable Y = new PersonalTable(Properties.Resources.personal_y, GameVersion.RBY);
private static byte[][] splitBytes(byte[] data, int size)
{

View file

@ -12324,12 +12324,15 @@ namespace PKHeX.Core.Properties {
/// Looks up a localized string similar to PKHeX - By Kaphotics
///http://projectpokemon.org/pkhex
///
///17/01/30 - New Update:
/// - Added: Control right-clicking a PKM slot (box, party, etc) now allows direct legality checking.
/// - Added: 6/7 detection preferential treatment &amp; other detection methods. Thanks sora10pls!
/// - Added: Remove All medals button now clears Unlocked/Complete flags even if not visible.
/// - Fixed: Badly constructed ShowdownSets throw less exceptions (hopefully none). Thanks Sonic Blader!
/// - Fixed: Cloning to all slots in a Gen1/2 [rest of string was truncated]&quot;;.
///17/02/25 - New Update:
/// - Legality:
/// - - Added: Legality checking for RBY Pokémon as pk1 and pk7+.
/// - - Fixed: More edge cases for legality checks.
/// - Batch Editor:
/// - - Added: Nickname clearing to batch editor (via .IsNicknamed=False).
/// - - Added: Legality filtering and bulk suggestions for Met Location, Current Moves &amp; Relearn Moves.
/// - - - Use $suggest to use suggested result from the legality analysis.
/// - - Changed: Properties are now sort [rest of string was truncated]&quot;;.
/// </summary>
public static string changelog {
get {
@ -12459,6 +12462,16 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] encounter_blue {
get {
object obj = ResourceManager.GetObject("encounter_blue", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -12489,6 +12502,26 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] encounter_rb_f {
get {
object obj = ResourceManager.GetObject("encounter_rb_f", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] encounter_red {
get {
object obj = ResourceManager.GetObject("encounter_red", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -12529,6 +12562,26 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] encounter_yellow {
get {
object obj = ResourceManager.GetObject("encounter_yellow", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] encounter_yellow_f {
get {
object obj = ResourceManager.GetObject("encounter_yellow_f", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -12539,6 +12592,26 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] evos_gsc {
get {
object obj = ResourceManager.GetObject("evos_gsc", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] evos_rby {
get {
object obj = ResourceManager.GetObject("evos_rby", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -17761,6 +17834,36 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] lvlmove_c {
get {
object obj = ResourceManager.GetObject("lvlmove_c", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] lvlmove_gs {
get {
object obj = ResourceManager.GetObject("lvlmove_gs", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] lvlmove_rb {
get {
object obj = ResourceManager.GetObject("lvlmove_rb", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -17781,6 +17884,16 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] lvlmove_y {
get {
object obj = ResourceManager.GetObject("lvlmove_y", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@ -17944,9 +18057,9 @@ namespace PKHeX.Core.Properties {
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] personal_rby {
public static byte[] personal_rb {
get {
object obj = ResourceManager.GetObject("personal_rby", resourceCulture);
object obj = ResourceManager.GetObject("personal_rb", resourceCulture);
return ((byte[])(obj));
}
}
@ -17981,6 +18094,16 @@ namespace PKHeX.Core.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] personal_y {
get {
object obj = ResourceManager.GetObject("personal_y", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@ -17992,7 +18115,7 @@ namespace PKHeX.Core.Properties {
}
/// <summary>
/// Looks up a localized string similar to 20170130.
/// Looks up a localized string similar to 20170225.
/// </summary>
public static string ProgramVersion {
get {
@ -19071,7 +19194,7 @@ namespace PKHeX.Core.Properties {
///// Main Window
///
///CTRL-O: Open
///CTRL-S: Export PK6
///CTRL-S: Export PKM
///CTRL-E: Export SAV
///CTRL-B: Export BAK
///CTRL-Q: Quit
@ -26683,8 +26806,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///Rote
///Blaue[INT]/Grüne[JP]
///Blaue
///Grüne
///Gelbe.
/// </summary>
public static string text_games_de {
@ -26730,8 +26853,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///Red
///Blue
///Green
///Blue[INT]/Green[JP]
///Blue[JP]
///Yellow.
/// </summary>
public static string text_games_en {
@ -26777,8 +26900,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///Roja
///Azul
///Verde
///Azul[INT]/Verde[JP]
///Azul[JP]
///Amarilla.
/// </summary>
public static string text_games_es {
@ -26824,8 +26947,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///Rouge
///Bleue
///Vert
///Bleue[INT]/Vert[JP]
///Bleue[JP]
///Jaune.
/// </summary>
public static string text_games_fr {
@ -26871,8 +26994,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///Rossa
///Blu
///Verde
///Blu[INT]/Verde[JP]
///Blu[JP]
///Gialla.
/// </summary>
public static string text_games_it {
@ -26918,8 +27041,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///赤
///青
///
///青[INT]/緑[JP]
///青[JP]
///黄.
/// </summary>
public static string text_games_ja {
@ -26965,8 +27088,8 @@ namespace PKHeX.Core.Properties {
///
///고
///레드
///블루
///그린
///블루[INT]/그린[JP]
///블루[JP]
///옐로.
/// </summary>
public static string text_games_ko {
@ -27012,8 +27135,8 @@ namespace PKHeX.Core.Properties {
///
///Go
///紅
///藍色
///綠色
///藍色[INT]/綠色[JP]
///藍色[JP]
///黃色.
/// </summary>
public static string text_games_zh {
@ -28889,7 +29012,7 @@ namespace PKHeX.Core.Properties {
}
/// <summary>
/// Looks up a localized string similar to Ningún objeto
/// Looks up a localized string similar to Ninguno
///Master Ball
///Ultra Ball
///Super Ball
@ -28930,7 +29053,7 @@ namespace PKHeX.Core.Properties {
///Éter
///Éter Máximo
///Elixir
///Elixir M [rest of string was truncated]&quot;;.
///Elixir Máximo /// [rest of string was truncated]&quot;;.
/// </summary>
public static string text_items_es {
get {
@ -34182,8 +34305,8 @@ namespace PKHeX.Core.Properties {
///小磁怪
///三合一磁怪
///大蔥鴨
///都都
///都都
///嘟嘟
///嘟嘟
///小海獅
///白海獅
///臭泥

View file

@ -6388,9 +6388,6 @@
<data name="ProgramVersion" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\text\version.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
<data name="personal_rby" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\personal_rby;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="text_gsc_00000_en" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\text\gen3\text_gsc_00000_en.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
</data>
@ -7384,4 +7381,43 @@
<data name="fashion_m_sm_illegal" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\fashion_m_sm_illegal;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="evos_rby" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\evos_rby.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="encounter_blue" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\encounter_blue.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="encounter_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\encounter_red.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="encounter_yellow" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\encounter_yellow.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="encounter_rb_f" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\encounter_rb_f.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="encounter_yellow_f" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\encounter_yellow_f.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="lvlmove_rb" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\lvlmove_rb.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="lvlmove_y" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\lvlmove_y.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="personal_rb" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\personal_rb;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="personal_y" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\personal_y;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="evos_gsc" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\evos_gsc.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="lvlmove_c" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\lvlmove_c.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="lvlmove_gs" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\byte\lvlmove_gs.pkl;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 533 B

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 B

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 577 B

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 429 B

View file

@ -1,7 +1,24 @@
PKHeX - By Kaphotics
http://projectpokemon.org/pkhex
17/02/07 - New Update:
17/02/25 - New Update:
- Legality:
- - Added: Legality checking for RBY Pokémon as pk1 and pk7+.
- - Fixed: More edge cases for legality checks.
- Batch Editor:
- - Added: Nickname clearing to batch editor (via .IsNicknamed=False).
- - Added: Legality filtering and bulk suggestions for Met Location, Current Moves & Relearn Moves.
- - - Use $suggest to use suggested result from the legality analysis.
- - Changed: Properties are now sorted alphabetically.
- Added: WC7 parsing for multiple-item gifts. Thanks SciresM!
- Added: X/Y Trainer Nickname editing (via Current Appearance tab).
- Added: S/M Festival Plaza name editing.
- Fixed: Gen4 Pokédex bitflag setting no longer sets invalid data for single-gender species.
- Fixed: wc7->pk7 base friendship fixed. Thanks T.O.R.N.A.D.O & Odaxis!
- Fixed: Gen1/2 Money/Coin editing & inventory editing edge cases.
- Updated: Menu sprites to use Sun/Moon sprites for some species (like Steelix).
17/02/07 - New Update: (64480) [360228]
- Legality:
- - Added: Legality indication for exported QR images (if legality check is available for origin).
- - Added: Legality indication for Box/Party pkm slots (^ + opt-in via Options->Set to SAV).

View file

@ -34,6 +34,6 @@ Mond
Go
Rote
Blaue[INT]/Grüne[JP]
Blaue
Grüne
Gelbe

View file

@ -34,6 +34,6 @@ Moon
Go
Red
Blue
Green
Blue[INT]/Green[JP]
Blue[JP]
Yellow

View file

@ -34,6 +34,6 @@ Luna
Go
Roja
Azul
Verde
Azul[INT]/Verde[JP]
Azul[JP]
Amarilla

View file

@ -34,6 +34,6 @@ Lune
Go
Rouge
Bleue
Vert
Bleue[INT]/Vert[JP]
Bleue[JP]
Jaune

View file

@ -34,6 +34,6 @@ Luna
Go
Rossa
Blu
Verde
Blu[INT]/Verde[JP]
Blu[JP]
Gialla

View file

@ -34,6 +34,6 @@ OR
Go
[INT]/緑[JP]
青[JP]

View file

@ -34,6 +34,6 @@ OR
레드
블루
그린
블루[INT]/그린[JP]
블루[JP]
옐로

View file

@ -38,7 +38,7 @@ Hold Control when dragging to save encrypted (ekx).
Click on...
- OT label: Set save file details to relevant properties.
- Met Location label: Suggest encounter information for relevant properties.
- Level / Met Location label: Suggest encounter information for relevant properties.
- Moves groupbox: Suggest legal moves.
- Relearn Moves groupbox: Suggest legal relearn moves.

View file

@ -1 +1 @@
20170207
20170225

View file

@ -34,6 +34,6 @@ Y
Go
藍色
綠色
藍色[INT]/綠色[JP]
藍色[JP]
黃色

View file

@ -82,8 +82,8 @@
小磁怪
三合一磁怪
大蔥鴨
都都
都都
嘟嘟
嘟嘟
小海獅
白海獅
臭泥
@ -110,7 +110,7 @@
瓦斯彈
雙彈瓦斯
獨角犀牛
角犀獸
角犀獸
吉利蛋
蔓藤怪
袋獸
@ -120,7 +120,7 @@
金魚王
海星星
寶石海星
人偶
人偶
飛天螳螂
迷唇姐
電擊獸
@ -213,7 +213,7 @@
巨鉗螳螂
壺壺
赫拉克羅斯
熊寶寶
圈圈熊
熔岩蟲
@ -262,7 +262,7 @@
土狼犬
大狼犬
蛇紋熊
刺尾蟲
甲殼繭
狩獵鳳蝶
@ -387,7 +387,7 @@
代歐奇希斯
草苗龜
樹林龜
小火焰猴
猛火猴
烈焰猴
@ -425,7 +425,7 @@
雙尾怪手
飄飄球
隨風球
卷卷
捲捲
長耳兔
夢妖魔
烏鴉頭頭
@ -454,12 +454,12 @@
不良蛙
毒骷蛙
尖牙籠
光魚
光魚
霓虹魚
小球飛魚
雪笠怪
暴雪王
自爆磁怪
大舌舔
超甲狂犀
@ -519,7 +519,7 @@
夢夢蝕
豆豆鴿
咕咕鴿
高傲
高傲
斑斑馬
雷電斑馬
石丸子
@ -542,7 +542,7 @@
寶包繭
保母蟲
百足蜈蚣
車輪
車輪
蜈蚣王
木棉球
風妖精
@ -559,7 +559,7 @@
岩殿居蟹
滑滑小子
頭巾混混
哭哭面具
死神棺
原蓋海龜
@ -639,7 +639,7 @@
勾帕路翁
代拉基翁
畢力吉翁
雷電雲
萊希拉姆
捷克羅姆
@ -701,7 +701,7 @@
仙子伊布
摔角鷹人
咚咚鼠
小碎
小碎
黏黏寶
黏美兒
黏美龍
@ -768,8 +768,8 @@
膽小蟲
具甲武者
沙丘娃
噬沙堡
拳海
噬沙堡
拳海
屬性:空
銀伴戰獸
小隕星
@ -785,7 +785,7 @@
杖尾鱗甲龍
卡璞・鳴鳴
卡璞・蝶蝶
卡璞・牟牟
卡璞・哞哞
卡璞・鰭鰭
科斯莫古
科斯莫姆
@ -796,7 +796,7 @@
費洛美螂
電束木
鐵火輝夜
惡食大王
奈克洛茲瑪
瑪機雅娜

View file

@ -33,7 +33,7 @@ namespace PKHeX.Core
Party = getPartyOffset(0);
Japanese = SaveUtil.getIsG1SAVJ(Data);
Personal = PersonalTable.RBY;
Personal = PersonalTable.Y;
// Stash boxes after the save file's end.
byte[] TempBox = new byte[SIZE_STOREDBOX];
@ -318,22 +318,23 @@ namespace PKHeX.Core
}
public override uint Money
{
get { return uint.Parse((BigEndian.ToUInt32(Data, Japanese ? 0x25EE : 0x25F3) >> 8).ToString("X6")); }
get { return (uint)BigEndian.BCDToInt32(Data, Japanese ? 0x25EE : 0x25F3, 3); }
set
{
BigEndian.GetBytes(Convert.ToUInt32(value.ToString("000000"), 16)).Skip(1).ToArray().CopyTo(Data, Japanese ? 0x25EE : 0x25F3);
value = (uint)Math.Min(value, MaxMoney);
BigEndian.Int32ToBCD((int)value, 3).CopyTo(Data, Japanese ? 0x25EE : 0x25F3);
}
}
public uint Coin
{
get
{
return uint.Parse(BigEndian.ToUInt16(Data, Japanese ? 0x2846 : 0x2850).ToString("X4"));
return (uint)BigEndian.BCDToInt32(Data, Japanese ? 0x2846 : 0x2850, 2);
}
set
{
ushort val = (ushort)Math.Min(value, MaxCoins);
BigEndian.GetBytes(val).CopyTo(Data, Japanese ? 0x2846 : 0x2850);
value = (ushort)Math.Min(value, MaxCoins);
BigEndian.Int32ToBCD((int)value, 2).CopyTo(Data, Japanese ? 0x2846 : 0x2850);
}
}

View file

@ -152,7 +152,7 @@ namespace PKHeX.Core
}
if (Version == GameVersion.C && Japanese)
{
Array.Copy(Data, 0x2009, Data, 0x7209, 0xB32);
Array.Copy(Data, 0x2009, Data, 0x7209, 0xADA);
}
if (Version == GameVersion.GS && !Japanese)
{
@ -257,96 +257,45 @@ namespace PKHeX.Core
private int GenderOffset { get; set; } = int.MinValue;
// Checksums
private ushort getChecksum()
{
int end;
switch (Version)
{
case GameVersion.C:
end = Japanese ? 0x2AE2 : 0x2B82;
break;
default: // GS
end = Japanese ? 0x2C8B : 0x2D68;
break;
}
return (ushort)Data.Skip(0x2009).Take(end - 0x2009 + 1).Sum(a => a);
}
protected override void setChecksums()
{
ushort accum = 0;
for (int i = 0x2009; i <= 0x2B3A; i++)
accum += Data[i];
if (Version == GameVersion.C && Japanese)
{
BitConverter.GetBytes(accum).CopyTo(Data, 0x2D0D);
BitConverter.GetBytes(accum).CopyTo(Data, 0x7F0D);
}
for (int i = 0x2B3B; i <= 0x2B82; i++)
accum += Data[i];
if (Version == GameVersion.C && !Japanese)
{
BitConverter.GetBytes(accum).CopyTo(Data, 0x2D0D);
BitConverter.GetBytes(accum).CopyTo(Data, 0x1F0D);
}
for (int i = 0x2B83; i <= 0x2C8B; i++)
accum += Data[i];
if (Version == GameVersion.GS && Japanese)
{
BitConverter.GetBytes(accum).CopyTo(Data, 0x2D0D);
BitConverter.GetBytes(accum).CopyTo(Data, 0x7F0D);
}
for (int i = 0x2C8C; i <= 0x2D68; i++)
accum += Data[i];
ushort accum = getChecksum();
if (Version == GameVersion.GS && !Japanese)
{
BitConverter.GetBytes(accum).CopyTo(Data, 0x2D69);
BitConverter.GetBytes(accum).CopyTo(Data, 0x7E6D);
}
else
{
BitConverter.GetBytes(accum).CopyTo(Data, 0x2D0D);
BitConverter.GetBytes(accum).CopyTo(Data, 0x7F0D);
}
}
public override bool ChecksumsValid
{
get
{
ushort accum = 0;
for (int i = 0x2009; i <= 0x2B3A; i++)
accum += Data[i];
if (Version == GameVersion.C && Japanese)
return accum == BitConverter.ToUInt16(Data, 0x2D0D); // Japanese Crystal
for (int i = 0x2B3B; i <= 0x2B82; i++)
accum += Data[i];
if (Version == GameVersion.C && !Japanese)
return accum == BitConverter.ToUInt16(Data, 0x2D0D); // US Crystal
for (int i = 0x2B83; i <= 0x2C8B; i++)
accum += Data[i];
if (Version == GameVersion.GS && Japanese)
return accum == BitConverter.ToUInt16(Data, 0x2D69); // Japanese Gold/Silver
for (int i = 0x2C8C; i <= 0x2D68; i++)
accum += Data[i];
ushort accum = getChecksum();
if (Version == GameVersion.GS && !Japanese)
return accum == BitConverter.ToUInt16(Data, 0x2D69); // US Gold/Silver
return false;
return accum == BitConverter.ToUInt16(Data, 0x2D0D); // Japanese Crystal
}
}
private int getChecksum()
{
ushort accum = 0;
for (int i = 0x2009; i <= 0x2B3A; i++)
accum += Data[i];
if (Version == GameVersion.C && Japanese)
return accum; // Japanese Crystal
for (int i = 0x2B3B; i <= 0x2B82; i++)
accum += Data[i];
if (Version == GameVersion.C && !Japanese)
return accum; // US Crystal
for (int i = 0x2B83; i <= 0x2C8B; i++)
accum += Data[i];
if (Version == GameVersion.GS && Japanese)
return accum; // Japanese Gold/Silver
for (int i = 0x2C8C; i <= 0x2D68; i++)
accum += Data[i];
if (Version == GameVersion.GS && !Japanese)
return accum; // US Gold/Silver
return 0;
}
public override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
// Trainer Info
@ -354,7 +303,7 @@ namespace PKHeX.Core
public override string OT
{
get { return PKX.getG1Str(Data.Skip(0x200B).Take(StringLength).ToArray(), Japanese); }
get { return PKX.getG1Str(getData(0x200B, StringLength), Japanese); }
set
{
byte[] strdata = PKX.setG1Str(value, Japanese);
@ -448,22 +397,23 @@ namespace PKHeX.Core
}
public override uint Money
{
get { return BigEndian.ToUInt32(Data, MoneyOffset) >> 8; }
get { return (uint)BigEndian.BCDToInt32(Data, MoneyOffset, 3); }
set
{
BigEndian.GetBytes(value > 999999 ? 999999 : value).Skip(1).ToArray().CopyTo(Data, MoneyOffset);
value = (uint)Math.Min(value, MaxMoney);
BigEndian.Int32ToBCD((int)value, 3).CopyTo(Data, MoneyOffset);
}
}
public uint Coin
{
get
{
return BigEndian.ToUInt16(Data, MoneyOffset + 7);
return (uint)BigEndian.BCDToInt32(Data, MoneyOffset + 7, 2);
}
set
{
ushort val = (ushort)Math.Min(value, MaxCoins);
BigEndian.GetBytes(val).CopyTo(Data, MoneyOffset + 7);
value = (ushort)Math.Min(value, MaxCoins);
BigEndian.Int32ToBCD((int)value, 2).CopyTo(Data, MoneyOffset + 7);
}
}
@ -563,7 +513,7 @@ namespace PKHeX.Core
}
public override string getBoxName(int box)
{
return PKX.getG1Str(Data.Skip(BoxNamesOffset + box*9).Take(9).ToArray(), Japanese);
return PKX.getG1Str(getData(BoxNamesOffset + box*9, 9), Japanese);
}
public override void setBoxName(int box, string value)
{
@ -624,10 +574,16 @@ namespace PKHeX.Core
int bit = pkm.Species - 1;
int ofs = bit >> 3;
byte bitval = (byte)(1 << (bit & 7));
if (!seen)
{
// Clear the Seen Flag
Data[PokedexSeenOffset + ofs] &= (byte)~bitval;
return;
}
// Set the Seen Flag
Data[PokedexSeenOffset + ofs] &= (byte)~bitval;
if (seen)
Data[PokedexSeenOffset + ofs] |= bitval;
Data[PokedexSeenOffset + ofs] |= bitval;
}
public override void setCaught(PKM pkm, bool caught = true)
{
@ -641,18 +597,22 @@ namespace PKHeX.Core
int bit = pkm.Species - 1;
int ofs = bit >> 3;
byte bitval = (byte)(1 << (bit & 7));
// Set the Captured Flag
Data[PokedexCaughtOffset + ofs] &= (byte)~bitval;
if (caught)
if (!caught)
{
Data[PokedexCaughtOffset + ofs] |= bitval;
if (pkm.Species == 201) // Unown
// Clear the Captured Flag
Data[PokedexCaughtOffset + ofs] &= (byte)~bitval;
return;
}
// Set the Captured Flag
Data[PokedexCaughtOffset + ofs] |= bitval;
if (pkm.Species == 201) // Unown
{
// Give all Unown caught to prevent a crash on pokedex view
for (int i = 1; i <= 26; i++)
{
// Give all Unown caught to prevent a crash on pokedex view
for (int i = 1; i <= 26; i++)
{
Data[PokedexSeenOffset + 0x1F + i] = (byte)i;
}
Data[PokedexSeenOffset + 0x1F + i] = (byte)i;
}
}
}

View file

@ -758,12 +758,26 @@ namespace PKHeX.Core
const int brSize = 0x40;
int bit = pkm.Species - 1;
byte mask = (byte) (1 << (bit&7));
int ofs = PokeDex + bit>>3 + 0x4;
// Set the Species Owned Flag
Data[PokeDex + brSize*0 + bit/8 + 0x4] |= (byte) (1 << (bit%8));
Data[ofs + brSize*0] |= mask;
// Set the Species Seen Flag
Data[PokeDex + brSize*1 + bit/8 + 0x4] |= (byte) (1 << (bit%8));
// Check if already Seen
int gr = pkm.PersonalInfo.Gender;
if (gr != 0 && gr < 254) // Not genderless or fixed gender
{
bool m = (Data[ofs + brSize*1] & mask) != 0;
bool f = (Data[ofs + brSize*2] & mask) != 0;
if (!(m || f)) // not yet seen
{
// Set OTHER gender seen bit so it appears second
int gender = pkm.Gender & 1;
gender ^= 1; // other gender
Data[ofs + brSize*(1 + gender)] |= mask;
}
}
int FormOffset1 = PokeDex + 0x108;
int PokeDexLanguageFlags = FormOffset1 + 0x20;

View file

@ -524,6 +524,16 @@ namespace PKHeX.Core
BitConverter.GetBytes(value).CopyTo(Data, JoinFestaData + 0x50C);
}
}
public string FestivalPlazaName
{
get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, JoinFestaData + 0x510, 0x2A)); }
set
{
const int max = 20;
if (value.Length > max) value = value.Substring(0, max);
Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, JoinFestaData + 0x510);
}
}
public override int PlayedHours
{

View file

@ -1002,5 +1002,74 @@ namespace PKHeX.Core
return new Tuple<uint, uint>(TID, SID);
}
/// <summary>
/// Creates a <see cref="SaveFile"/> via decryption using a stored xorpad.
/// </summary>
/// <param name="input">Encrypted byte array of savedata to decrypt.</param>
/// <param name="XORpads">Array of possible paths to check for xorpad compatibility.</param>
/// <returns>Returns a <see cref="SaveFile"/> if decryption was successful, else null.</returns>
public static SaveFile getSAVfromXORpads(byte[] input, string[] XORpads)
{
byte[] savID = new byte[0x10];
Array.Copy(input, 0x10, savID, 0, 0x10);
int[] sizes = { SIZE_G6XY, SIZE_G6ORAS, SIZE_G7SM };
foreach (var file in XORpads)
{
// Check if xorpad
FileInfo fi = new FileInfo(file);
string filename = fi.Name.ToLower();
if (!filename.Contains("xorpad") && !filename.Contains("key"))
continue;
var length = fi.Length;
if (length != 0x10009C && length != 0x100000)
continue;
// Fix xorpad alignment
byte[] xorpad = File.ReadAllBytes(file);
if (xorpad.Length == 0x10009C) // Trim off Powersaves' header
{
Array.Copy(xorpad, 0x9C, xorpad, 0, 0x100000);
Array.Resize(ref xorpad, 0x100000);
}
// Check if encrypted 00's match save
if (!xorpad.Skip(0x10).Take(0x10).SequenceEqual(savID))
continue;
// Set up Decrypted File
const int mainOffset = 0x5400;
int maxSize = sizes.Max();
byte[] decryptedPS = new byte[maxSize];
Array.Copy(input, mainOffset, decryptedPS, 0, decryptedPS.Length);
// xor through and decrypt
for (int z = 0; z < decryptedPS.Length; z++)
decryptedPS[z] ^= xorpad[mainOffset + z];
// Weakly check the validity of the decrypted content
int i; for (i = 0; i < sizes.Length; i++)
{
if (BitConverter.ToUInt32(decryptedPS, sizes[i] - 0x1F0) != BEEF)
continue;
Array.Resize(ref decryptedPS, sizes[i]);
break;
}
if (i == sizes.Length)
continue;
// Save file is now decrypted!
var SAV = getVariantSAV(decryptedPS);
if (SAV == null)
continue;
SAV.FileName = file;
return SAV;
}
return null; // no xorpad compatible
}
}
}

View file

@ -175,6 +175,8 @@ namespace PKHeX.Core
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)

View file

@ -63,5 +63,40 @@ namespace PKHeX.Core
result[--i] = data[o++];
return result;
}
/// <summary>
/// Returns a 32-bit signed integer converted from bytes in a Binary Coded Decimal format byte array.
/// </summary>
/// <param name="input">Input byte array to read from.</param>
/// <param name="offset">Offset to start reading at.</param>
/// <param name="length">Length of array to read.</param>
public static int BCDToInt32(byte[] input, int offset, int length)
{
int result = 0;
for (int i = offset; i < offset + length; i++)
{
byte p = input[i];
result *= 100;
result += 10 * (p >> 4);
result += p & 0xf;
}
return result;
}
/// <summary>
/// Returns the specified 32-bit signed integer value as an array of Binary Coded Decimal format bytes.
/// </summary>
/// <param name="input">32-bit signed integer to convert.</param>
/// <param name="size">Desired size of returned array.</param>
public static byte[] Int32ToBCD(int input, int size)
{
byte[] result = new byte[size];
for (int i = 0; i < size; i++)
{
int p = input%100;
input /= 100;
result[size - i - 1] = (byte)(p/10 << 4 | p%10);
}
return result;
}
}
}

View file

@ -5,9 +5,9 @@ PKHeX
Pokémon core series save editor, programmed in [C#](https://en.wikipedia.org/wiki/C_Sharp_%28programming_language%29).
Supports the following files:
* Save files ("main", .sav)
* Individual Pokémon entity files (.pk*)
* Mystery Gift files (.pgt, .pcd, .pgf, .wc*) including conversion to .pk*
* Save files ("main", \*.sav, \*.dsv, \*.dat, \*.gci)
* Individual Pokémon entity files (.pk\*)
* Mystery Gift files (.pgt, .pcd, .pgf, .wc\*) including conversion to .pk\*
* Importing teams from Decrypted 3DS Battle Videos
* Transferring from one generation to another, converting formats along the way.
@ -16,7 +16,9 @@ The interface can be translated with resource/external text files so that differ
Pokémon Showdown sets and QR codes can be imported/exported to assist in sharing.
Nintendo 3DS savedata containers use an AES MAC that cannot be emulated without the 3DS's keys, thus a resigning service is required (svdt, save_manager, or SaveDataFiler).
Nintendo 3DS savedata containers use an AES MAC that cannot be emulated without the 3DS's keys, thus a resigning service is required ([svdt](https://github.com/meladroit/svdt), save_manager, [JKSM](https://github.com/J-D-K/JKSM), or SaveDataFiler).
**We do not support or condone cheating at the expense of others. Do not use significantly hacked Pokémon in battle or in trades with those who are unaware hacked Pokémon are in use.**
## Screenshots
@ -42,4 +44,4 @@ PKHeX can be opened with IDEs such as [Visual Studio](https://www.visualstudio.c
### GNU/Linux
Install MonoDevelop and [Mono](http://www.mono-project.com/) runtime with `flatpak install --user --from https://download.mono-project.com/repo/monodevelop.flatpakref`. GNU/Linux is not the main Operating System of developers of this program so there may be bugs; some may come from non GNU/Linux specific code of Mono (so developers using *BSD, Windows and OS X should be able to reproduce them).
Install [MonoDevelop](http://www.monodevelop.com/) and [Mono Runtime](http://www.mono-project.com/) with `flatpak install --user --from https://download.mono-project.com/repo/monodevelop.flatpakref`. GNU/Linux is not the main Operating System of developers of this program so there may be bugs; some may come from non GNU/Linux specific code of Mono (so developers using *BSD, Windows and OS X should be able to reproduce them).