private string ROMCharactersToString(int MaxStringLength, uint StringLocation, byte TerminatorByte = 0xFF)
        {
            // From https://github.com/Joexv/IPES/blob/master/IPES/Form1.cs
            // Accessed 16/03/2016

            string result = "";                                                                        // Initialize the string to be returned.

            using (GBABinaryReader RomRead = new GBABinaryReader(LoadedRom))                           // Access the ROM as a readable file.
            {
                for (int CurrentCharacter = 0; CurrentCharacter < MaxStringLength; CurrentCharacter++) // Loop through the set length of the string.
                {
                    RomRead.BaseStream.Seek(StringLocation + CurrentCharacter, SeekOrigin.Begin);      // Go to the current character's ROM location.
                    byte CharacterByte = RomRead.ReadByte();                                           // Read the byte of the next character.
                    if ((CharacterByte != TerminatorByte))                                             // If it is not the terminator...
                    {
                        char temp    = ';';
                        bool success = CharacterValues.TryGetValue(CharacterByte, out temp); // ... then try to retrieve its ASCII equivalent from the dictionary.
                        if (success)
                        {
                            result += temp; // If the character was successfully retrieved, add it to the string to be returned.
                        }
                    }
                    else // If the terminator has been reached, the string is to be assumed complete, so exit the loop.
                    {
                        break;
                    }
                }
            }
            return(result);
        }
        private void ReadRomData(string FileName)
        {
            using (GBABinaryReader RomRead = new GBABinaryReader(File.OpenRead(FileName))) // Access the ROM as a readable file.
            {
                RomRead.BaseStream.Seek(GameCodeLocation, SeekOrigin.Begin);               // Go to the location of the ROM code.
                string code = Encoding.UTF8.GetString(RomRead.ReadBytes(4));               // Read the ROM code.

                LoadedRom.File = FileName;
                LoadedRom.Code = code;
            }
        }
        private void EditCompatibility(char BitToChangeTo, int PokemonToChange, int MoveNumber)
        {
            // Assume TM/HM editing to be the default.
            int PointerToCompatibilityTable = PointerToTMCompatibilityTable;
            int SizeOfEntryInBits           = 58;

            switch (CurrentMode)
            {
            case "TM":
            case "HM":
            default:
                break;

            case "Tutor":
                PointerToCompatibilityTable = PointerToMoveTutorCompatibilityTable;
                SizeOfEntryInBits           = NumberOfMoveTutorMoves;
                break;
            }

            using (GBABinaryReader RomRead = new GBABinaryReader(LoadedRom))                                                       // Access the ROM as a readable file.
            {
                RomRead.BaseStream.Seek(PointerToCompatibilityTable, SeekOrigin.Begin);                                            // Go to the pointer to the compatibility table.
                uint CompatibilityTable = RomRead.ReadPointer();                                                                   // Read the pointer.

                RomRead.BaseStream.Seek(CompatibilityTable + (PokemonToChange * SizeOfCompatibilityTableEntry), SeekOrigin.Begin); // Go to the specified Pokémon's entry in the compatibility table.
                ulong hexCompatibilityEntry;                                                                                       // Needs to be a ulong to support the 64bit TM/HM compatibility entries.
                switch (CurrentMode)
                {
                case "TM":
                case "HM":
                default:
                    hexCompatibilityEntry = RomRead.ReadUInt64();     // Read the specified Pokémon's TM/HM compatibility entry. It is eight bytes long, so is a 64bit integer.
                    break;

                case "Tutor":
                    switch (LoadedRom.Code)
                    {
                    case "BPRE":
                    default:
                        hexCompatibilityEntry = RomRead.ReadUInt16();         // Read the specified Pokémon's move tutor compatibility entry. It is two bytes long, so is a 16bit integer.
                        break;

                    case "BPEE":
                        hexCompatibilityEntry = RomRead.ReadUInt32();         // In Emerald, the move tutor compatibility entries are four bytes long, and so are 32bit integers.
                        break;
                    }
                    break;
                }

                string stringCompatibilityEntry = ReverseString(ConvertToBinary(hexCompatibilityEntry).PadLeft(SizeOfEntryInBits, '0')); // Convert the compatibility entry to binary, as it is structured as a bitfield.
                // As the leading zeroes will not be there, pad the left of the string with 0s to make it the size of the entry in bits.
                // The bitfield is backwards, so reverse it.

                char[] CompatibilityWithMove = stringCompatibilityEntry.ToCharArray(); // Convert this string into an chararray.
                // This allows access of individual characters, and thereby modify individual entries in the bitfield.
                // The chararray is 0-indexed, so:
                // CompatibilityWithMove[0] = TM1 / Move Tutor move 1
                // CompatibilityWithMove[14] = TM15 / Move Tutor move 15
                // CompatibilityWithMove[49] = TM50
                // CompatibilityWithMove[50] = HM01 --> CompatibilityWithMove[57] = HM08

                CompatibilityWithMove[MoveNumber] = BitToChangeTo; // Update the specified compatibility with the move to be the specified value.
                // 0 for no compatibility, 1 for compatibility.

                stringCompatibilityEntry = new String(CompatibilityWithMove);                                // Revert the chararray back into a string.

                stringCompatibilityEntry = BinaryStringToHexString(ReverseString(stringCompatibilityEntry)); // The bitfield needs to be backwards again, so reverse it.
                // Convert it back into hex from binary.

                byte[] bytearrayCompatibilityEntry = StringToByteArray(stringCompatibilityEntry);                                       // Convert the string into a bytearray, to be reinserted into the ROM.
                Array.Reverse(bytearrayCompatibilityEntry);                                                                             // Reverse it once more to accommodate the ROM's endianness.

                using (GBABinaryWriter RomWrite = new GBABinaryWriter(LoadedRom))                                                       // Access the ROM as a writeable ROM.
                {
                    RomWrite.BaseStream.Seek(CompatibilityTable + (PokemonToChange * SizeOfCompatibilityTableEntry), SeekOrigin.Begin); // Go to the specified Pokémon's entry in the compatibility table.
                    RomWrite.Write(bytearrayCompatibilityEntry);                                                                        // Write the new compatibility entry to the ROM.
                }
            }
        }
        private void UpdateROMData()
        {
            // Retrieve the number of Pokémon species in the ROM.
            using (GBABinaryReader RomRead = new GBABinaryReader(LoadedRom))         // Access the ROM as a readable file.
            {
                RomRead.BaseStream.Seek(PointerToNumberOfSpecies, SeekOrigin.Begin); // Go to the pointer to the number of species.
                NumberOfSpecies = RomRead.ReadUInt16();                              // Read the number of species. It is two bytes long, so is a 16bit integer.
            }

            numPokemonMin.Value = 0;
            numPokemonMax.Value = NumberOfSpecies;

            // Clear the move lists.
            ListOfMoveNames.Clear();
            ListOfMoves.Clear();

            int PointerToMoveList   = PointerToTMMoveList; // Take the TM as default.
            int NumberOfMovesInList = 50;

            switch (CurrentMode)
            {
            case "TM":     // TMs are taken as default, so just exit the switch.
            default:
                break;

            case "HM":                   // HMs are a part of the TM move list, so the PointerToMoveList is the same.
                NumberOfMovesInList = 8; // However, there are only eight HMs, so change the NumberOfMovesInList to reflect this.
                break;

            case "Tutor":
                PointerToMoveList   = PointerToMoveTutorMoveList;
                NumberOfMovesInList = NumberOfMoveTutorMoves;
                break;
            }

            using (GBABinaryReader RomRead = new GBABinaryReader(LoadedRom))  // Access the ROM as a readable file.
            {
                RomRead.BaseStream.Seek(PointerToMoveList, SeekOrigin.Begin); // Go to the pointer to the move list.
                uint MoveList = RomRead.ReadPointer();                        // Read the pointer.

                if (CurrentMode == "HM")
                {
                    MoveList += 50 * 2; // Skip past the TMs to the HM list.
                }

                RomRead.BaseStream.Seek(PointerToMoveNames, SeekOrigin.Begin);
                uint MoveNames = RomRead.ReadPointer();
                for (uint i = 0; i < 0x1FF; i++)                                       // Loop through every move and retrieve its name.
                {
                    string MoveName = ROMCharactersToString(0xD, MoveNames + i * 0xD); // Retrieve the name from its ROM location and convert it from the Pokémon character encoding to ASCII.
                    ListOfMoveNames.Add(MoveName);                                     // Add the name to the list.
                }

                RomRead.BaseStream.Seek(MoveList, SeekOrigin.Begin); // Go to the move list.
                for (int i = 0; i < NumberOfMovesInList; i++)
                {
                    int MoveId = RomRead.ReadUInt16(); // Read the 2-byte little endian move ID.
                    if (MoveId <= 0x1FF)
                    {
                        ListOfMoves.Add(ListOfMoveNames[MoveId]); // Add the name of the move MoveId.
                    }
                    else
                    {
                        break; // If move ID is too large, or the list delimiter has been reached, exit the loop.
                    }
                }
            }

            lblSelectedMoveRemove.Text = ListOfMoves[Convert.ToInt32(numTMNumberRemove.Value) - 1]; // Update the form.
            lblSelectedMoveAdd.Text    = ListOfMoves[Convert.ToInt32(numTMNumberAdd.Value) - 1];
        }