bgneal@4: #ifndef CPP_ENIGMA_MACHINE_H bgneal@4: #define CPP_ENIGMA_MACHINE_H bgneal@4: // Copyright (C) 2012 by Brian Neal. bgneal@4: // This file is part of Cpp-Enigma, the Enigma Machine simulation. bgneal@4: // Cpp-Enigma is released under the MIT License (see License.txt). bgneal@4: // bgneal@4: // machine.h - This file contains the main Enigma machine class. bgneal@4: bgneal@4: #include bgneal@4: #include bgneal@4: #include bgneal@4: #include bgneal@4: #include "enigma_types.h" bgneal@4: #include "rotor.h" bgneal@4: #include "plugboard.h" bgneal@4: bgneal@4: namespace enigma bgneal@4: { bgneal@14: typedef std::vector rotor_vector; bgneal@4: bgneal@4: class enigma_machine_error : public enigma_error bgneal@4: { bgneal@4: public: bgneal@4: explicit enigma_machine_error(const std::string& what_arg) bgneal@4: : enigma_error(what_arg) bgneal@4: {} bgneal@4: }; bgneal@4: bgneal@4: class enigma_machine bgneal@4: { bgneal@4: public: bgneal@14: // Construct an Enigma machine from component parts. bgneal@14: // Note that the enigma_machine makes copies of the rotors and will not bgneal@14: // delete the rotor pointers: bgneal@11: enigma_machine(const rotor_vector& rv, bgneal@14: const rotor& reflector, bgneal@4: const plugboard& pb); bgneal@4: bgneal@4: // construct an Enigma machine with a default plugboard (no cables connected): bgneal@11: enigma_machine(const rotor_vector& rv, bgneal@14: const rotor& reflector); bgneal@4: bgneal@4: // key-sheet style constructors: bgneal@4: enigma_machine(const std::vector& rotor_types, bgneal@4: const std::vector& ring_settings, bgneal@4: const std::string& reflector_name = "B", bgneal@4: const std::string& plugboard_settings = ""); bgneal@4: bgneal@4: // set the rotor display (starting position) - 3 rotor version bgneal@4: void set_display(char left, char mid, char right) bgneal@4: { bgneal@13: assert(rotors.size() == 4); bgneal@4: bgneal@13: rotors[1].set_display(left); bgneal@13: rotors[2].set_display(mid); bgneal@13: rotors[3].set_display(right); bgneal@4: } bgneal@4: bgneal@4: // set the rotor display (starting position) - 4 rotor version bgneal@4: void set_display(char c0, char c1, char c2, char c3) bgneal@4: { bgneal@13: assert(rotors.size() == 5); bgneal@4: bgneal@13: rotors[1].set_display(c0); bgneal@13: rotors[2].set_display(c1); bgneal@13: rotors[3].set_display(c2); bgneal@13: rotors[4].set_display(c3); bgneal@4: } bgneal@4: bgneal@7: // Set the rotor display (starting position) using a string; the bgneal@7: // string length must match the number of rotors in use or a bgneal@7: // enigma_machine_error exception will be thrown: bgneal@7: void set_display(const std::string& val) bgneal@7: { bgneal@13: if (val.size() == 3 && rotors.size() == 4) bgneal@7: { bgneal@7: set_display(val[0], val[1], val[2]); bgneal@7: } bgneal@13: else if (val.size() == 4 && rotors.size() == 5) bgneal@7: { bgneal@7: set_display(val[0], val[1], val[2], val[3]); bgneal@7: } bgneal@7: else bgneal@7: { bgneal@7: throw enigma_machine_error("set_display invalid size"); bgneal@7: } bgneal@7: } bgneal@7: bgneal@4: // return the rotor display (starting position) as a string bgneal@4: std::string get_display() const bgneal@4: { bgneal@4: std::string result; bgneal@13: for (std::size_t i = 1; i < rotors.size(); ++i) bgneal@4: { bgneal@13: result += rotors[i].get_display(); bgneal@4: } bgneal@4: return result; bgneal@4: } bgneal@4: bgneal@4: // simulate front panel key press; returns the lamp character that is lit bgneal@4: char key_press(char c) bgneal@4: { bgneal@4: step_rotors(); bgneal@4: return electric_signal(c - 'A') + 'A'; bgneal@4: } bgneal@4: bgneal@15: // this is like key_press(), but it works in signal numbers (0-25) instead of chars: bgneal@15: int step(int n) bgneal@15: { bgneal@15: step_rotors(); bgneal@15: return electric_signal(n); bgneal@15: } bgneal@15: bgneal@4: // Process a buffer of text of length n, placing the result in an output buffer. bgneal@4: void process_text(const char* input, char* output, std::size_t n) bgneal@4: { bgneal@4: for (std::size_t i = 0; i < n; ++i) bgneal@4: { bgneal@4: *output++ = key_press(*input++); bgneal@4: } bgneal@4: } bgneal@4: bgneal@4: std::string process_text(const std::string& input) bgneal@4: { bgneal@4: std::string result; bgneal@4: result.reserve(input.size()); bgneal@4: bgneal@4: for (const auto& c : input) bgneal@4: { bgneal@4: result += key_press(c); bgneal@4: } bgneal@4: return result; bgneal@4: } bgneal@4: bgneal@12: // Process a buffer of pre-processed text of length n, placing the result in an output buffer. bgneal@12: void process_data(const char* input, char* output, std::size_t n) bgneal@12: { bgneal@12: for (std::size_t i = 0; i < n; ++i) bgneal@12: { bgneal@12: step_rotors(); bgneal@12: *output++ = electric_signal(*input++) + 'A'; bgneal@12: } bgneal@12: } bgneal@12: bgneal@4: // for access to the plugboard for hill-climbing, etc bgneal@4: plugboard& get_plugboard() { return pb; } bgneal@4: bgneal@8: // Returns a string representation of the enigma machine's state. Useful bgneal@8: // for logging, etc: bgneal@8: // bgneal@8: std::string army_str() const { return str(true); } bgneal@8: std::string navy_str() const { return str(false); } bgneal@8: bgneal@4: private: bgneal@13: // Note that to improve cache performance, the rotors and reflectors are stored bgneal@13: // in a contiguous vector. bgneal@13: std::vector rotors; // rotor & reflector array bgneal@4: plugboard pb; bgneal@4: rotor* r_rotor; // rightmost rotor bgneal@4: rotor* m_rotor; // 2nd to right rotor bgneal@4: rotor* l_rotor; // 3rd to right rotor bgneal@4: bgneal@4: void rotor_count_check(); bgneal@4: bgneal@4: void step_rotors() bgneal@4: { bgneal@4: // The right-most rotor's right-side ratchet is always over a pawl, and bgneal@4: // it has no neighbor to the right, so it always rotates. bgneal@4: // bgneal@4: // The middle rotor will rotate if either: bgneal@4: // 1) The right-most rotor's left side notch is over the 2nd pawl bgneal@4: // or bgneal@4: // 2) It has a left-side notch over the 3rd pawl bgneal@4: // bgneal@4: // The third rotor (from the right) will rotate only if the middle rotor bgneal@4: // has a left-side notch over the 3rd pawl. bgneal@4: // bgneal@4: // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost) bgneal@4: // does not rotate (they did not add a 4th pawl to the mechanism). bgneal@4: bgneal@4: const bool l_rotate = m_rotor->notch_over_pawl(); bgneal@4: const bool m_rotate = l_rotate || r_rotor->notch_over_pawl(); bgneal@4: bgneal@4: r_rotor->rotate(); bgneal@4: if (m_rotate) bgneal@4: { bgneal@4: m_rotor->rotate(); bgneal@4: } bgneal@4: if (l_rotate) bgneal@4: { bgneal@4: l_rotor->rotate(); bgneal@4: } bgneal@4: } bgneal@4: bgneal@4: // Simulate running an electric signal through the machine in order to bgneal@4: // perform an encrypt or decrypt operation bgneal@4: // signal_num - the wire (0-25) that the simulated current occurs on bgneal@4: // Returns a lamp number to light (an integer 0-25). bgneal@4: int electric_signal(int signal_num) bgneal@4: { bgneal@13: int n = pb.signal(signal_num); bgneal@4: bgneal@13: if (rotors.size() == 4) // 3 rotors + reflector bgneal@4: { bgneal@13: n = rotors[3].signal_in(n); bgneal@13: n = rotors[2].signal_in(n); bgneal@13: n = rotors[1].signal_in(n); bgneal@13: n = rotors[0].signal_in(n); // reflector bgneal@13: n = rotors[1].signal_out(n); bgneal@13: n = rotors[2].signal_out(n); bgneal@13: n = rotors[3].signal_out(n); bgneal@4: } bgneal@13: else // Kriegsmarine 4 rotor + reflector bgneal@4: { bgneal@13: n = rotors[4].signal_in(n); bgneal@13: n = rotors[3].signal_in(n); bgneal@13: n = rotors[2].signal_in(n); bgneal@13: n = rotors[1].signal_in(n); bgneal@13: n = rotors[0].signal_in(n); // reflector bgneal@13: n = rotors[1].signal_out(n); bgneal@13: n = rotors[2].signal_out(n); bgneal@13: n = rotors[3].signal_out(n); bgneal@13: n = rotors[4].signal_out(n); bgneal@4: } bgneal@13: return pb.signal(n); bgneal@4: } bgneal@8: bgneal@8: std::string str(bool army) const; bgneal@4: }; bgneal@4: } bgneal@4: bgneal@4: #endif