/// <summary>
        /// Converts a password into mail information.
        /// </summary>
        /// <param name="password">The password to convert.</param>
        /// <returns>Mail information from the password.</returns>
        public static MissionMail Convert(string password)
        {
            if (string.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException(nameof(password));
            }

            // Sanitaze and check password
            password = password.Replace(" ", "");
            password = password.Replace(Environment.NewLine, "");
            password = password.ToUpper();
            if (password.Length != PasswordLength)
            {
                throw new ArgumentException("Invalid password length");
            }

            // Do decryption rounds
            // The last byte for "scramble" is ignored. It should be the null
            // terminator 0x00.
            password = Permutation.Decrypt(password, true);
            byte[] binary = Substitution.Decrypt(password);
            Scramble.Decrypt(binary[0], binary, 1, binary.Length - 2);

            // Validate checksum
            byte checksum    = binary[0]; // The scramble key is the checksum too.
            byte newChecksum = Checksum.Calculate(binary, 1, binary.Length - 1);

            if (checksum != newChecksum)
            {
                throw new FormatException("Invalid checksum");
            }

            // Convert the binary password into the structure.
            // Write the array into a stream to use the BitReader.
            DataStream stream = new DataStream();

            stream.Write(binary, 1, binary.Length - 1);
            BitReader reader = new BitReader(stream);

            MissionMail info = new MissionMail();

            info.Type           = (MissionState)reader.ReadByte(4);
            info.LocationId     = reader.ReadByte(7);
            info.FloorNumber    = reader.ReadByte(7);
            info.Random         = (info.Type == MissionState.Sos) ? reader.ReadUInt32(24) : 0x00;
            info.UID            = reader.ReadUInt64(64);
            info.ClientLanguage = (GameLanguage)reader.ReadByte(4);
            info.ClientName     = reader.ReadString(80, EncodingName);
            info.ObjectID1      = (info.Type == MissionState.Sos) ? (ushort)0x00 : reader.ReadUInt16(10);
            info.ObjectID2      = (info.Type == MissionState.Sos) ? (ushort)0x00 : reader.ReadUInt16(10);
            info.RescuerUID     = reader.ReadUInt64(64);
            info.GameType       = (GameType)reader.ReadByte(2);

            return(info);
        }
        /// <summary>
        /// Converts a missiong information into a password.
        /// </summary>
        /// <param name="info">Mission to convert.</param>
        /// <returns>The password.</returns>
        public static string Convert(MissionMail info)
        {
            if (info == null)
            {
                throw new ArgumentNullException(nameof(info));
            }

            // Serialize the structure into a bit stream
            DataStream stream = new DataStream();
            BitWriter  writer = new BitWriter(stream);

            writer.Write((byte)info.Type, 4);
            writer.Write(info.LocationId, 7);
            writer.Write(info.FloorNumber, 7);
            if (info.Type == MissionState.Sos)
            {
                writer.Write(info.Random, 24);
            }
            writer.Write(info.UID, 64);
            writer.Write((byte)info.ClientLanguage, 4);
            writer.Write(info.ClientName, 80, EncodingName);
            if (info.Type != MissionState.Sos)
            {
                writer.Write(info.ObjectID1, 10);
                writer.Write(info.ObjectID2, 10);
            }

            writer.Write(info.RescuerUID, 64);
            writer.Write((byte)info.GameType, 2);

            // Write the stream into an array for the rounds.
            // We allocate an extra space for the checksum (first byte)
            // and the null terminator (last byte).
            byte[] binary = new byte[stream.Length + 2];
            stream.Position = 0;
            stream.Read(binary, 1, binary.Length - 2);

            // Create checksum
            byte checksum = Checksum.Calculate(binary, 1, binary.Length - 1);

            binary[0] = checksum;

            // Do encryption rounds
            // The key is the checksum, we don't encrypt the null terminator.
            Scramble.Encrypt(checksum, binary, 1, binary.Length - 2);
            string password = Substitution.Encrypt(binary, PasswordLength);

            password = Permutation.Encrypt(password, true);

            return(password);
        }
        /// <summary>
        /// Main entry point.
        /// </summary>
        /// <param name="args">Program arguments.</param>
        static void Main(string[] args)
        {
            Console.WriteLine(
                "DungeonKey v{0} -- KeyGen for Pokemon Mystery Dungeon ~~ by pleonex",
                Assembly.GetExecutingAssembly().GetName().Version);

            string password;

            if (args.Length == 0)
            {
                Console.Write("SOS-mail password: "******"SOS-mail password: "******"Error converting password.");
                Console.WriteLine("Make sure the password is valid.");
                Exit(1);
            }

            sosMail.PrintInformation();
            Console.WriteLine();

            if (MissionMailConverter.Convert(sosMail) == password)
            {
                Console.WriteLine("\x1B[32m✔\x1B[0m EXACT password generated!");
            }
            else
            {
                Console.WriteLine("\x1B[91m✖\x1B[0m FAILED to generate pasword");
            }

            Console.WriteLine("Generating rescue mail");
            AckMail ackMail = new AckMail(sosMail)
            {
                GameType       = GameType.Time,
                ClientLanguage = GameLanguage.English,
                ClientName     = "pleonex",
                ObjectID1      = 0,
                ObjectID2      = 0x25
            };

            ackMail.PrintInformation();

            string ackPassword = MissionMailConverter.Convert(ackMail);

            Console.WriteLine("Rescue password: "******"\x1B[32m✔\x1B[0m CORRECT password generated!");
            }
            else
            {
                Console.WriteLine("\x1B[91m✖\x1B[0m FAILED to generate pasword");
            }

            ThankYouMail thanksMail = new ThankYouMail(ackMail)
            {
                GameType       = GameType.Sky,
                ClientLanguage = GameLanguage.English,
                ClientName     = "pleonex!!",
                ObjectID1      = 0,
                ObjectID2      = 0x2C
            };

            thanksMail.PrintInformation();

            string thanksPassword = MissionMailConverter.Convert(thanksMail);

            Console.WriteLine("Thank-YOU password: "******"\x1B[32m✔\x1B[0m CORRECT password generated!");
            }
            else
            {
                Console.WriteLine("\x1B[91m✖\x1B[0m FAILED to generate pasword");
            }

            Exit(0);
        }