/// <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); }