} //...should allow PSSE to work longer without needing an update. #endregion Properties public Database(bool shwmsg = false, bool dev = false) { //if a new resource file is needed, don't forget to add a line to Resource_Popup's TLP ! string[] filenames = { "megaStone.bin", "pokemonData.bin", "stageData.bin", "stageDataEvent.bin", "stageDataExtra.bin", "pokemonLevel.bin", "pokemonAbility.bin", "missionCard.bin", "messagePokedex_US.bin", "pokeLoad.bin" }; string resourcedir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + "resources" + Path.DirectorySeparatorChar; bool[] overRide = new bool[filenames.Length]; for (int i = 0; i < overRide.Length; i++) { overRide[i] = true; } if (shwmsg) { string[] fn = new string[filenames.Length]; for (int i = 0; i < fn.Length; i++) { fn[i] = filenames[i]; } Array.Sort(fn, (x, y) => String.Compare(x, y)); using (var form = new Resources_Popup(fn, resourcedir, dev)) { form.ShowDialog(); if (form.DialogResult == DialogResult.OK && dev) { for (int i = 0; i < overRide.Length; i++) { overRide[i] = form.retChk[Array.IndexOf(fn, filenames[i])]; } } } } //bin init MegaStoneBin = Properties.Resources.megaStone; MissionCardBin = Properties.Resources.missionCard; MonAbilityBin = Properties.Resources.pokemonAbility; MonDataBin = Properties.Resources.pokemonData; MonLevelBin = Properties.Resources.pokemonLevel; StagesMainBin = Properties.Resources.stageData; StagesEventBin = Properties.Resources.stageDataEvent; StagesExpertBin = Properties.Resources.stageDataExtra; MessageDexBin = Properties.Resources.messagePokedex_US; PokeLoadBin = Properties.Resources.pokeLoad; //resources override if (Directory.Exists(resourcedir)) { for (int i = 0; i < filenames.Length; i++) { if (File.Exists(resourcedir + filenames[i]) && overRide[i]) { switch (i) //don't forget that part or resources files won't override Database files, add an entry if a file is added above { case 0: MegaStoneBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 1: MonDataBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 2: StagesMainBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 3: StagesEventBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 4: StagesExpertBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 5: MonLevelBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 6: MonAbilityBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 7: MissionCardBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 8: MessageDexBin = File.ReadAllBytes(resourcedir + filenames[i]); break; case 9: PokeLoadBin = File.ReadAllBytes(resourcedir + filenames[i]); break; default: MessageBox.Show("Error loading resources :\nfilename = " + (filenames[i] != null ? filenames[i] : "null") + "\ni = " + i); break; } } } } //txt init SpeciesList = Properties.Resources.species.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); MonsList = Properties.Resources.mons.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); //PokathlonList = Properties.Resources.pokathlon.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); MegaStartIndex = MonsList.ToList().IndexOf("Mega Venusaur"); MonStopIndex = MonsList.ToList().IndexOf("---", 1); //megas int entrylen = BitConverter.ToInt32(MonDataBin, 0x4); Megas = new Tuple <int, int> [BitConverter.ToUInt32(MegaStoneBin, 0) - 1]; for (int i = 0; i < Megas.Length; i++) { int monIndex = BitConverter.ToUInt16(MegaStoneBin, MegaStoneBin[0x10] + (i + 1) * 4) & 0x3FF; string str = "Mega " + MonsList[monIndex]; int spec = (BitConverter.ToInt32(MonDataBin.Skip(0x50 + entrylen * monIndex).Take(entrylen).ToArray(), 0xE) >> 6) & 0x7FF; if (spec == 6 || spec == 150) //if specie is Mewtwo/Charizard, specify if entry is for X or Y stone. { str += (monIndex != (BitConverter.ToUInt16(MegaStoneBin, MegaStoneBin[0x10] + i * 4) & 0x3FF)) ? " X" : " Y"; } byte[] data = MonDataBin.Skip(0x50 + entrylen * MonsList.ToList().IndexOf(str)).Take(entrylen).ToArray(); int maxSpeedup = (BitConverter.ToInt32(data, 0xA) >> 7) & 0x7F; Megas[i] = new Tuple <int, int>(monIndex, maxSpeedup); } MegaList = new List <int>(); for (int i = 0; i < Megas.Length; i++) { MegaList.Add(Megas[i].Item1); } HasMega = new bool[MonsList.Length][]; for (int i = 0; i < MonsList.Length; i++) { HasMega[i] = new bool[2]; } for (int i = 0; i < Megas.Length; i++) { HasMega[BitConverter.ToUInt16(MegaStoneBin, 0x54 + i * 4) & 0x3FF][(MegaStoneBin[0x54 + (i * 4) + 1] >> 3) & 1] = true; } //pokemons Forms = new int[SpeciesList.Length]; Mons = new dbMon[BitConverter.ToUInt32(MonDataBin, 0)]; for (int i = 0; i < Mons.Length; i++) { byte[] data = MonDataBin.Skip(0x50 + entrylen * i).Take(entrylen).ToArray(); bool isMega = i >= MegaStartIndex && i <= MonsList.Count() - 1; int spec = (isMega && i <= MegaStartIndex + Megas.Length - 1) ? SpeciesList.ToList().IndexOf(MonsList[Megas[i - MegaStartIndex].Item1].Substring(0, (MonsList[Megas[i - MegaStartIndex].Item1].LastIndexOf(' ') <= 0) ? MonsList[Megas[i - MegaStartIndex].Item1].Length : MonsList[Megas[i - MegaStartIndex].Item1].LastIndexOf(' '))) : (BitConverter.ToInt32(data, 0xE) >> 6) & 0x7FF; int raiseMaxLevel = (BitConverter.ToInt16(data, 0x4)) & 0x3F; int basePower = (BitConverter.ToInt16(data, 0x3)) & 0x7; //ranges 1-7 for now (30-90 BP), may need an update later on int[] skillsadr = new int[] { 0x02, 0x20, 0x21, 0x22, 0x23 }, skill = new int[skillsadr.Length]; int j = 0, skillCount = 0; foreach (int adr in skillsadr) { skill[j] = data[adr]; //ranges 1-~130 for now, ordered list in MESSAGE_XX/message_PokedexXX.bin ("Opportunist" to "Transform" then a bunch more with a lot of placeholders) if (skill[j] != 0) { skillCount++; } j++; } skillCount = Math.Max(skillCount, 1); int type = (BitConverter.ToInt16(data, 0x01) >> 3) & 0x1F; //ranges 0-17 (normal - fairy) (https://gbatemp.net/threads/psse-pokemon-shuffle-save-editor.396499/page-33#post-6278446) int index = (BitConverter.ToInt16(data, 0)) & 0x3FF; //ranges 1-999, it's the number you can see on the team selection menu Mons[i] = new dbMon(spec, Forms[spec], isMega, raiseMaxLevel, basePower, skill, type, index, skillCount); Forms[spec]++; } //Survival mode int smEntry = BitConverter.ToInt32(PokeLoadBin, 0x4), smSkip = BitConverter.ToInt32(PokeLoadBin, 0x10), smTake = BitConverter.ToInt32(PokeLoadBin, 0x14); Pokathlon = new List <int> [BitConverter.ToInt16(PokeLoadBin.Skip(smSkip + smTake - smEntry).Take(smEntry).ToArray(), 0) & 0x3FF]; //# of entries doesn't match # of steps since some are collided so I take the last entry and read its 'lowStep' value (should compare to 'highStep' but I don't want to overcomplicate thigns for now) for (int i = 0; i < BitConverter.ToInt32(PokeLoadBin, 0); i++) { byte[] data = PokeLoadBin.Skip(smSkip + i * smEntry).Take(smEntry).ToArray(); int lowStep = BitConverter.ToInt16(data, 0) & 0x3FF, highStep = (BitConverter.ToInt16(data, 0x01) >> 2) & 0x3FF; //if highStep !=0 then data[] applies to all steps in the lowStep - highStep range int min = (BitConverter.ToInt16(data, 0x02) >> 4) & 0xFFF, max = BitConverter.ToInt16(data, 0x04) & 0xFFF; //if max !=0 then all stages in min-max range are possibilities for corresponding step(s) List <int> stagesList = Enumerable.Range(min, max != 0 ? max - min + 1 : 1).ToList(); for (int j = 0x08; j < (data.Length - 3); j += 4) //weird pattern for excluded stages : each 32-bits block starting at 0x08 contains 3 10-bits long stages # { int exception = 0; for (int w = 0; w < 3; w++) { exception = (BitConverter.ToInt32(data, j) >> (w * 10)) & 0x3FF; if (exception == 0) { break; } else if (stagesList.Contains(exception)) { stagesList.Remove(exception); } } if (exception == 0) { break; } } foreach (int step in Enumerable.Range(lowStep, 1 + Math.Max(0, highStep - lowStep))) { Pokathlon[step - 1] = stagesList; } } #region old Survival //pokathlon //PokathlonRand = new int[PokathlonList.Length / 2][]; //for (int i = 0; i < PokathlonRand.Length; i++) //{ // PokathlonRand[i] = new int[2]; // Int32.TryParse(PokathlonList[2 * i], out PokathlonRand[i][0]); // Int32.TryParse(PokathlonList[1 + 2 * i], out PokathlonRand[i][1]); //} #endregion //missions Missions = new bool[BitConverter.ToInt32(MissionCardBin, 0)][]; for (int i = 0; i < Missions.Length; i++) { Missions[i] = new bool[10]; int ientrylen = BitConverter.ToInt32(MissionCardBin, 0x4); byte[] data = MissionCardBin.Skip(BitConverter.ToInt32(MissionCardBin, 0x10) + i * ientrylen).Take(ientrylen).ToArray(); for (int j = 0; j < Missions[i].Length; j++) { Missions[i][j] = BitConverter.ToInt16(data, 0x8 + 2 * j) != 0; } } //dictionnary (new) string temp = Encoding.Unicode.GetString(MessageDexBin.Skip(BitConverter.ToInt32(MessageDexBin, 0x08)).Take(BitConverter.ToInt32(MessageDexBin, 0x0C) - 0x17).ToArray()); //Relevant chunk specified in .bin file, UTF16 Encoding, 0x17 bytes at the end are a useless stamp (data.messagePokedex) temp = temp.Replace(Encoding.Unicode.GetString(MessageDexBin.Skip(BitConverter.ToInt32(MessageDexBin, 0x08)).Take(0x10).ToArray()), "[name]"); //because this variable ends with 0x00 it messes with Split() later on, so I replace it here temp = temp.Replace(Encoding.Unicode.GetString(new byte[] { 0x01, 0x00, 0x03, 0x01, 0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x6D, 0x65, 0x67, 0x61, 0x4E, 0x61, 0x6D, 0x65, 0x00, 0x00 }), "[megaName]"); //same but this variable isn't declared on a fixed position so I copied it directly string[] arr = temp.Split((char)0x00); //split the single string in an array arr = arr.Skip(Array.IndexOf(arr, "Opportunist")).ToArray(); //we only care for skills so I get rid of anything before Opportunist for (int i = 0; i < arr.Length; i++) { if (String.IsNullOrEmpty(arr[i])) { arr[i] = "-Placeholder-"; //make sure there is no empty strings just in case } } /* This code below separates Skills entries from Text entries while ignoring a few mega-skills entries : * Right now (1.5.7) the list of strings looks like that : [Skills1][Text for Skills1][Text for mega skills][Skills2][Text for Skills2][Skills3][Text for Skills3]. * If another group of [Skills][Text for skills] is ever added this will need a 3rd string to concatenate. * Also, note that there is no [Mega Skills], which is why I didn't implement it yet (the names of Mega Skills are probably inside another file). */ string[] hardcoded = new string[] { "Opportunist", "Transform", "Big Wave", "Super Cheer", "Not Caught", "Hammering Streak" }; //add hardcoded strings here int[] indexes = new int[hardcoded.Length]; for (int i = 0; i < indexes.Length; i++) { indexes[i] = Array.IndexOf(arr, hardcoded[i]) + (i % 2); //I add 1 to every odd entry because they correspond to the first text of a group whereas I use the hardcoded last skill of former group instead. } string[][] stringChunks = new string[indexes.Length][]; int chunksLength = 0; for (int i = 0; i < stringChunks.Length; i++) { if (i == 4) { stringChunks[i] = arr.Skip(indexes[i] + 1).Take(indexes[2 * (i / 2) + 1] - indexes[2 * (i / 2)] - 1).ToArray(); //because for some reason in v1.5.7 skill "Not Caught" doesn't have a flavour entry so I had to remove it manually so the texts match correctly. } else { stringChunks[i] = arr.Skip(indexes[i] + ((i == 4) ? 1 : 0)).Take(indexes[2 * (i / 2) + 1] - indexes[2 * (i / 2)]).ToArray(); } if (i % 2 == 1) { chunksLength += Math.Max(stringChunks[i].Length, stringChunks[i - 1].Length); } } string[][] skillText = new string[2][]; //[0] = skill name / [1] = flavor text for (int i = 0; i < skillText.Length; i++) { skillText[i] = new string[chunksLength]; } for (int i = 0; i < stringChunks.Length; i++) { stringChunks[i].CopyTo(skillText[i % 2], Array.IndexOf(skillText[i % 2], null)); } SkillsList = skillText[0]; SkillsTextList = skillText[1]; //stages --> there are a lot of things that could be added here Stages = new dbStage[3][]; Stages[0] = new dbStage[(BitConverter.ToInt32(StagesMainBin, 0) - 1) * 2]; //Main + UX stages Stages[1] = new dbStage[BitConverter.ToInt32(StagesExpertBin, 0)]; //EX stages Stages[2] = new dbStage[BitConverter.ToInt32(StagesEventBin, 0)]; //Event stages for (int i = 0; i < Stages.Length; i++) { byte[] stagesData = new byte[][] { StagesMainBin, StagesExpertBin, StagesEventBin }[i]; int stgEntrylen = BitConverter.ToInt32(stagesData, 0x4); for (int j = 0; j < Stages[i].Length; j++) { int index = j; bool isux = (i == 0 && j >= (Stages[i].Length / 2)); if (i == 0) { if (isux) { index -= Stages[i].Length / 2; } index++; } byte[] data = stagesData.Skip(0x50 + stgEntrylen * index).Take(stgEntrylen).ToArray(); int pkmn = BitConverter.ToInt16(data, 0) & 0x7FF; int sranks = BitConverter.ToInt16(data, 0x4C) & 0x3FF; //999 = unreleased EX stage, anything else = number of S ranks (EX stages) or completed stages (Main stages) required for that level to be unlocked int hp = (int)(BitConverter.ToUInt64(data, 0x4) & 0xFFFFFF) * (isux ? 3 : 1); int[] minmoves = new int[4]; //required number of moves left for C, B, A, S rank for (int k = 0; k < minmoves.Length; k++) { minmoves[k] = ((k > 0) ? ((BitConverter.ToInt16(data, 0x30 + k - 1) >> 4) & 0xFF) : 0); //data only has numbers for A, B & S ranks since C rank always require 0 moves left } Stages[i][j] = new dbStage(isux, pkmn, sranks, hp, minmoves); } } }
} //...should allow PSSE to work longer without needing an update. #endregion Properties public Database() { //bin init MegaStone = Properties.Resources.megaStone; MissionCard = Properties.Resources.missionCard; MonAbility = Properties.Resources.pokemonAbility; MonData = Properties.Resources.pokemonData; MonLevel = Properties.Resources.pokemonLevel; StagesMain = Properties.Resources.stageData; StagesEvent = Properties.Resources.stageDataEvent; StagesExpert = Properties.Resources.stageDataExtra; byte[][] files = { MegaStone, MonData, StagesMain, StagesEvent, StagesExpert, MonLevel, MonAbility, MissionCard }; string[] filenames = { "megaStone.bin", "pokemonData.bin", "stageData.bin", "stageDataEvent.bin", "stageDataExtra.bin", "pokemonLevel.bin", "pokemonAbility.bin", "missionCard.bin" }; string resourcedir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + "resources" + Path.DirectorySeparatorChar; #region old "resource" code //I don't want PSSE to populate the resource folder by itself anymore but it could still be handy //if (!Directory.Exists(resourcedir)) // Directory.CreateDirectory(resourcedir); //for (int i = 0; i < files.Length; i++) //{ // if (!File.Exists(resourcedir + filenames[i])) // File.WriteAllBytes(resourcedir + filenames[i], files[i]); // else // files[i] = File.ReadAllBytes(resourcedir + filenames[i]); //} #endregion if (Directory.Exists(resourcedir)) { for (int i = 0; i < files.Length; i++) { if (File.Exists(resourcedir + filenames[i])) { files[i] = File.ReadAllBytes(resourcedir + filenames[i]); } } } //txt init SpeciesList = Properties.Resources.species.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); MonsList = Properties.Resources.mons.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); PokathlonList = Properties.Resources.pokathlon.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); MegaStartIndex = MonsList.ToList().IndexOf("Mega Venusaur"); MonStopIndex = MonsList.ToList().IndexOf("---", 1); //megas int entrylen = BitConverter.ToInt32(MonData, 0x4); Megas = new Tuple <int, int> [BitConverter.ToUInt32(MegaStone, 0) - 1]; for (int i = 0; i < Megas.Length; i++) { int monIndex = BitConverter.ToUInt16(MegaStone, MegaStone[0x10] + (i + 1) * 4) & 0x3FF; string str = "Mega " + MonsList[monIndex]; if (monIndex == 6 || monIndex == 150) { str += (monIndex != (BitConverter.ToUInt16(MegaStone, MegaStone[0x10] + i * 4) & 0x3FF)) ? " X" : " Y"; } byte[] data = MonData.Skip(0x50 + entrylen * MonsList.ToList().IndexOf(str)).Take(entrylen).ToArray(); int maxSpeedup = (BitConverter.ToInt32(data, 0xA) >> 7) & 0x7F; Megas[i] = new Tuple <int, int>(monIndex, maxSpeedup); } MegaList = new List <int>(); for (int i = 0; i < Megas.Length; i++) { MegaList.Add(Megas[i].Item1); } HasMega = new bool[MonsList.Length][]; for (int i = 0; i < MonsList.Length; i++) { HasMega[i] = new bool[2]; } for (int i = 0; i < Megas.Length; i++) { HasMega[BitConverter.ToUInt16(MegaStone, 0x54 + i * 4) & 0x3FF][(MegaStone[0x54 + (i * 4) + 1] >> 3) & 1] = true; } //pokemons Forms = new int[SpeciesList.Length]; Mons = new Tuple <int, int, bool, int, int, int[], int, Tuple <int, int> > [BitConverter.ToUInt32(MonData, 0)]; Rest = new Tuple <int, int> [Mons.Length]; for (int i = 0; i < Mons.Length; i++) { byte[] data = MonData.Skip(0x50 + entrylen * i).Take(entrylen).ToArray(); bool isMega = i >= MegaStartIndex && i <= MonsList.Count() - 1; int spec = (isMega && i <= MegaStartIndex + Megas.Length - 1) ? SpeciesList.ToList().IndexOf(MonsList[Megas[i - MegaStartIndex].Item1].Substring(0, (MonsList[Megas[i - MegaStartIndex].Item1].LastIndexOf(' ') <= 0) ? MonsList[Megas[i - MegaStartIndex].Item1].Length : MonsList[Megas[i - MegaStartIndex].Item1].LastIndexOf(' '))) : (BitConverter.ToInt32(data, 0xE) >> 6) & 0x7FF; int raiseMaxLevel = (BitConverter.ToInt16(data, 0x4)) & 0x3F; int basePower = (BitConverter.ToInt16(data, 0x3)) & 0x7; //ranges 1-7 for now (30-90 BP), may need an update later on int[] skillsadr = new int[] { 0x02, 0x20, 0x21, 0x22, 0x23 }, skill = new int[skillsadr.Length]; int j = 0, skillCount = 0; foreach (int adr in skillsadr) { skill[j] = data[adr] & 0x7F; //ranges 1-~100 for now ("Opportunist" to "Transform"), ordered list in MESSAGE_XX/message_PokedexXX.bin if (skill[j] != 0) { skillCount++; } j++; } skillCount = Math.Max(skillCount, 1); int type = (BitConverter.ToInt16(data, 0x01) >> 3) & 0x1F; //ranges 0-17 (normal - fairy) (https://gbatemp.net/threads/psse-pokemon-shuffle-save-editor.396499/page-33#post-6278446) int index = (BitConverter.ToInt16(data, 0)) & 0x3FF; //ranges 1-999, it's the number you can see on the team selection menu Rest[i] = new Tuple <int, int>(index, skillCount); //Mons has more than 7 arguments so 8th one and beyond must be included in another Tuple Mons[i] = new Tuple <int, int, bool, int, int, int[], int, Tuple <int, int> >(spec, Forms[spec], isMega, raiseMaxLevel, basePower, skill, type, Rest[i]); Forms[spec]++; } //pokathlon PokathlonRand = new int[PokathlonList.Length / 2][]; for (int i = 0; i < PokathlonRand.Length; i++) { PokathlonRand[i] = new int[2]; Int32.TryParse(PokathlonList[2 * i], out PokathlonRand[i][0]); Int32.TryParse(PokathlonList[1 + 2 * i], out PokathlonRand[i][1]); } //missions Missions = new bool[BitConverter.ToInt32(MissionCard, 0)][]; for (int i = 0; i < Missions.Length; i++) { Missions[i] = new bool[10]; int ientrylen = BitConverter.ToInt32(MissionCard, 0x4); byte[] data = MissionCard.Skip(BitConverter.ToInt32(MissionCard, 0x10) + i * ientrylen).Take(ientrylen).ToArray(); for (int j = 0; j < Missions[i].Length; j++) { Missions[i][j] = BitConverter.ToInt16(data, 0x8 + 2 * j) != 0; } } //dictionnary, this is some really bad code here byte[] HexValue = Properties.Resources.messagePokedex_US; string StrValue = ""; List <string> List = new List <string>(); for (int i = 0; i < HexValue.Length; i += 2) { if (BitConverter.ToChar(HexValue, i) == '\0' && StrValue != "" && !(StrValue.EndsWith("\u0001ă\u0001\u0003\u0003慮敭") || StrValue.EndsWith("\u0001ă\u0001\u0003\u0005敭慧慎敭"))) { List.Add(StrValue.Replace("\u0001ă\u0001\u0003\u0003慮敭\0", "[name]").Replace("\u0001ă\u0001\u0003\u0005敭慧慎敭\0", "[name]")); StrValue = ""; } else { StrValue += BitConverter.ToChar(HexValue, i); } } SkillsList = List.Skip(List.IndexOf("Opportunist")).Take(List.IndexOf("Attacks can occasionally deal\ngreater damage than usual.") - List.IndexOf("Opportunist")).ToArray(); SkillsTextList = List.Skip(List.IndexOf("Attacks can occasionally deal\ngreater damage than usual.")).Take(List.IndexOf("Attacks can occasionally deal\ngreater damage than usual.") - List.IndexOf("Opportunist")).ToArray(); }
} //...should allow PSSE to work longer without needing an update. #endregion Properties public Database(bool shwmsg = false, bool dev = false) { //if a new resource file is needed, don't forget to add to Resource_Popup's TLP ! string[] filenames = { "megaStone.bin", "pokemonData.bin", "stageData.bin", "stageDataEvent.bin", "stageDataExtra.bin", "pokemonLevel.bin", "pokemonAbility.bin", "missionCard.bin", "messagePokedex_US.bin", "pokeLoad.bin" }; string resourcedir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + "resources" + Path.DirectorySeparatorChar; bool[] overRide = new bool[filenames.Length]; for (int i = 0; i < overRide.Length; i++) { overRide[i] = true; } if (shwmsg) { string[] fn = new string[filenames.Length]; for (int i = 0; i < fn.Length; i++) { fn[i] = filenames[i]; } Array.Sort(fn, (x, y) => String.Compare(x, y)); using (var form = new Resources_Popup(fn, resourcedir, dev)) { form.ShowDialog(); if (form.DialogResult != DialogResult.OK) { return; } else if (dev) { for (int i = 0; i < overRide.Length; i++) { overRide[i] = form.retChk[Array.IndexOf(fn, filenames[i])]; } } } //string blabla = null; //List<string> found = new List<string>(); //if (!Directory.Exists(resourcedir)) // blabla = "No resources folder found.\nCreate a new folder in the same directory as PSSE and name it exactly \"resources\".\n"; //else //{ // blabla = "A \"resources\" folder has been found"; // foreach (string file in filenames) // if (File.Exists(resourcedir + file)) { found.Add("\n\t" + file + "\t" + File.GetLastWriteTime(resourcedir + file)); } // if (found != null) // { // blabla += ".\n\nFiles found :"; // found.Sort(); // foreach (string str in found) // blabla += str; // blabla += "\n"; // } // else blabla += ", but it looks empty.\n"; //} //blabla += ("\nClick OK to use " + ((found == null) ? "built-in files" : "those files") + ", or use Abort to, well, abort."); //var result = MessageBox.Show(blabla + "\nPlease click the Help button below for more informations.", "Resources scan", MessageBoxButtons.OKCancel, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, 0, "https://github.com/supercarotte/PSSE/wiki/Extract-needed-resource-files-from-the-game"); //if (result != DialogResult.OK) // return; } //bin init MegaStone = Properties.Resources.megaStone; MissionCard = Properties.Resources.missionCard; MonAbility = Properties.Resources.pokemonAbility; MonData = Properties.Resources.pokemonData; MonLevel = Properties.Resources.pokemonLevel; StagesMain = Properties.Resources.stageData; StagesEvent = Properties.Resources.stageDataEvent; StagesExpert = Properties.Resources.stageDataExtra; MessageDex = Properties.Resources.messagePokedex_US; PokeLoad = Properties.Resources.pokeLoad; //resources override if (Directory.Exists(resourcedir)) { for (int i = 0; i < filenames.Length; i++) { if (File.Exists(resourcedir + filenames[i]) && overRide[i]) { switch (i) //don't forget that part or resources files won't override Database files, add an entry if a file is added above { case 0: MegaStone = File.ReadAllBytes(resourcedir + filenames[i]); break; case 1: MonData = File.ReadAllBytes(resourcedir + filenames[i]); break; case 2: StagesMain = File.ReadAllBytes(resourcedir + filenames[i]); break; case 3: StagesEvent = File.ReadAllBytes(resourcedir + filenames[i]); break; case 4: StagesExpert = File.ReadAllBytes(resourcedir + filenames[i]); break; case 5: MonLevel = File.ReadAllBytes(resourcedir + filenames[i]); break; case 6: MonAbility = File.ReadAllBytes(resourcedir + filenames[i]); break; case 7: MissionCard = File.ReadAllBytes(resourcedir + filenames[i]); break; case 8: MessageDex = File.ReadAllBytes(resourcedir + filenames[i]); break; case 9: PokeLoad = File.ReadAllBytes(resourcedir + filenames[i]); break; default: MessageBox.Show("Error loading resources :\nfilename = " + (filenames[i] != null ? filenames[i] : "null") + "\ni = " + i); break; } } } } //txt init SpeciesList = Properties.Resources.species.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); MonsList = Properties.Resources.mons.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); //PokathlonList = Properties.Resources.pokathlon.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); MegaStartIndex = MonsList.ToList().IndexOf("Mega Venusaur"); MonStopIndex = MonsList.ToList().IndexOf("---", 1); //megas int entrylen = BitConverter.ToInt32(MonData, 0x4); Megas = new Tuple <int, int> [BitConverter.ToUInt32(MegaStone, 0) - 1]; for (int i = 0; i < Megas.Length; i++) { int monIndex = BitConverter.ToUInt16(MegaStone, MegaStone[0x10] + (i + 1) * 4) & 0x3FF; string str = "Mega " + MonsList[monIndex]; if (monIndex == 6 || monIndex == 150) { str += (monIndex != (BitConverter.ToUInt16(MegaStone, MegaStone[0x10] + i * 4) & 0x3FF)) ? " X" : " Y"; } byte[] data = MonData.Skip(0x50 + entrylen * MonsList.ToList().IndexOf(str)).Take(entrylen).ToArray(); int maxSpeedup = (BitConverter.ToInt32(data, 0xA) >> 7) & 0x7F; Megas[i] = new Tuple <int, int>(monIndex, maxSpeedup); } MegaList = new List <int>(); for (int i = 0; i < Megas.Length; i++) { MegaList.Add(Megas[i].Item1); } HasMega = new bool[MonsList.Length][]; for (int i = 0; i < MonsList.Length; i++) { HasMega[i] = new bool[2]; } for (int i = 0; i < Megas.Length; i++) { HasMega[BitConverter.ToUInt16(MegaStone, 0x54 + i * 4) & 0x3FF][(MegaStone[0x54 + (i * 4) + 1] >> 3) & 1] = true; } //pokemons Forms = new int[SpeciesList.Length]; Mons = new Tuple <int, int, bool, int, int, int[], int, Tuple <int, int> > [BitConverter.ToUInt32(MonData, 0)]; Rest = new Tuple <int, int> [Mons.Length]; for (int i = 0; i < Mons.Length; i++) { byte[] data = MonData.Skip(0x50 + entrylen * i).Take(entrylen).ToArray(); bool isMega = i >= MegaStartIndex && i <= MonsList.Count() - 1; int spec = (isMega && i <= MegaStartIndex + Megas.Length - 1) ? SpeciesList.ToList().IndexOf(MonsList[Megas[i - MegaStartIndex].Item1].Substring(0, (MonsList[Megas[i - MegaStartIndex].Item1].LastIndexOf(' ') <= 0) ? MonsList[Megas[i - MegaStartIndex].Item1].Length : MonsList[Megas[i - MegaStartIndex].Item1].LastIndexOf(' '))) : (BitConverter.ToInt32(data, 0xE) >> 6) & 0x7FF; int raiseMaxLevel = (BitConverter.ToInt16(data, 0x4)) & 0x3F; int basePower = (BitConverter.ToInt16(data, 0x3)) & 0x7; //ranges 1-7 for now (30-90 BP), may need an update later on int[] skillsadr = new int[] { 0x02, 0x20, 0x21, 0x22, 0x23 }, skill = new int[skillsadr.Length]; int j = 0, skillCount = 0; foreach (int adr in skillsadr) { skill[j] = data[adr] & 0x7F; //ranges 1-~130 for now, ordered list in MESSAGE_XX/message_PokedexXX.bin ("Opportunist" to "Transform" then a bunch more with a lot of placeholders) if (skill[j] != 0) { skillCount++; } j++; } skillCount = Math.Max(skillCount, 1); int type = (BitConverter.ToInt16(data, 0x01) >> 3) & 0x1F; //ranges 0-17 (normal - fairy) (https://gbatemp.net/threads/psse-pokemon-shuffle-save-editor.396499/page-33#post-6278446) int index = (BitConverter.ToInt16(data, 0)) & 0x3FF; //ranges 1-999, it's the number you can see on the team selection menu Rest[i] = new Tuple <int, int>(index, skillCount); //Mons has more than 7 arguments so 8th one and beyond have to be included in another Tuple Mons[i] = new Tuple <int, int, bool, int, int, int[], int, Tuple <int, int> >(spec, Forms[spec], isMega, raiseMaxLevel, basePower, skill, type, Rest[i]); Forms[spec]++; } //Survival mode int smEntry = BitConverter.ToInt32(PokeLoad, 0x4), smSkip = BitConverter.ToInt32(PokeLoad, 0x10), smTake = BitConverter.ToInt32(PokeLoad, 0x14); Pokathlon = new List <int> [BitConverter.ToInt16(PokeLoad.Skip(smSkip + smTake - smEntry).Take(smEntry).ToArray(), 0) & 0x3FF]; //# of entries doesn't match # of steps since some are collided so I take the last entry and read its 'lowStep' value (should compare to 'highStep' but I don't want to overcomplicate thigns for now) for (int i = 0; i < BitConverter.ToInt32(PokeLoad, 0); i++) { byte[] data = PokeLoad.Skip(smSkip + i * smEntry).Take(smEntry).ToArray(); int lowStep = BitConverter.ToInt16(data, 0) & 0x3FF, highStep = (BitConverter.ToInt16(data, 0x01) >> 2) & 0x3FF; //if highStep !=0 then data[] applies to all steps in the lowStep - highStep range int min = (BitConverter.ToInt16(data, 0x02) >> 4) & 0xFFF, max = BitConverter.ToInt16(data, 0x04) & 0xFFF; //if max !=0 then all stages in min-max range are possibilities for corresponding step(s) List <int> stagesList = Enumerable.Range(min, max != 0 ? max - min + 1 : 1).ToList(); for (int j = 0x08; j < (data.Length - 3); j += 4) //weird pattern for excluded stages : each 32-bits block starting at 0x08 contains 3 10-bits long stages # { int exception = 0; for (int w = 0; w < 3; w++) { exception = (BitConverter.ToInt32(data, j) >> (w * 10)) & 0x3FF; if (exception == 0) { break; } else if (stagesList.Contains(exception)) { stagesList.Remove(exception); } } if (exception == 0) { break; } } foreach (int step in Enumerable.Range(lowStep, 1 + Math.Max(0, highStep - lowStep))) { Pokathlon[step - 1] = stagesList; } } #region old Survival //pokathlon //PokathlonRand = new int[PokathlonList.Length / 2][]; //for (int i = 0; i < PokathlonRand.Length; i++) //{ // PokathlonRand[i] = new int[2]; // Int32.TryParse(PokathlonList[2 * i], out PokathlonRand[i][0]); // Int32.TryParse(PokathlonList[1 + 2 * i], out PokathlonRand[i][1]); //} #endregion //missions Missions = new bool[BitConverter.ToInt32(MissionCard, 0)][]; for (int i = 0; i < Missions.Length; i++) { Missions[i] = new bool[10]; int ientrylen = BitConverter.ToInt32(MissionCard, 0x4); byte[] data = MissionCard.Skip(BitConverter.ToInt32(MissionCard, 0x10) + i * ientrylen).Take(ientrylen).ToArray(); for (int j = 0; j < Missions[i].Length; j++) { Missions[i][j] = BitConverter.ToInt16(data, 0x8 + 2 * j) != 0; } } //dictionnary (new) string temp = Encoding.Unicode.GetString(MessageDex.Skip(BitConverter.ToInt32(MessageDex, 0x08)).Take(BitConverter.ToInt32(MessageDex, 0x0C) - 0x17).ToArray()); //Relevant chunk specified in .bin file, UTF16 Encoding, 17 bytes at the end are a useless stamp (data.messagePokedex) temp = temp.Replace(Encoding.Unicode.GetString(MessageDex.Skip(BitConverter.ToInt32(MessageDex, 0x08)).Take(0x10).ToArray()), "[name]"); //because this variable ends with 0x00 it messes with Split() later on, so I replace it here temp = temp.Replace(Encoding.Unicode.GetString(new byte[] { 0x01, 0x00, 0x03, 0x01, 0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x6D, 0x65, 0x67, 0x61, 0x4E, 0x61, 0x6D, 0x65, 0x00, 0x00 }), "[megaName]"); //same but this variable isn't declared on a fixed position so I copied it directly string[] arr = temp.Split((char)0x00); //split the single string in an array arr = arr.Skip(Array.IndexOf(arr, "Opportunist")).ToArray(); //we only care for skills so I get rid of anything before Opportunist for (int i = 0; i < arr.Length; i++) { if (String.IsNullOrEmpty(arr[i])) { arr[i] = "-Placeholder-"; //make sure there is no empty strings just in case } } /* This code below separates Skills entries from Text entries while ignoring a few mega-skills entries * Right now (1.4.19) the list of strings looks like that : [Skills1][Text for Skills1][Text for mega skills][Skills2][Text for Skills2] * It shouldn't be a problem is more skills are added to [Skills2] (after all placeholders have been filled), but if another [Text for mega skills] is ever added this will need a 3rd string to concatenate * Also, note that there is no [Mega Skills], so if I ever want to implement them the same way I did normal skills another resource file will be needed. */ int a = Array.IndexOf(arr, "Opportunist"), b = Array.IndexOf(arr, "Rarely, attacks can deal\ngreater damage than usual."), c = Array.IndexOf(arr, "Big Wave"), d = Array.IndexOf(arr, "Increases damage done by\nany Water types in a combo."); string[] s1 = arr.Skip(a).Take(b - a).ToArray(), s2 = arr.Skip(c).Take(d - c).ToArray(), Skills = new string[s1.Length + s2.Length]; string[] st1 = arr.Skip(b).Take(b - a).ToArray(), st2 = arr.Skip(d).Take(d - c).ToArray(), SkillsT = new string[st1.Length + st2.Length]; s1.CopyTo(Skills, 0); s2.CopyTo(Skills, s1.Length); SkillsList = Skills; st1.CopyTo(SkillsT, 0); st2.CopyTo(SkillsT, s1.Length); SkillsTextList = SkillsT; }