// Cryp.cs : C# driver for CrypC

//                                 Data Encryption
//                                  Fast & Secure
//
//                             ----------------------
//                      The algorithm for the Data Encryption
//                   Standard runs too slow on most micros, but
//                    simpler methods have not provided secure
//                      encryption. This program solves this
//                     problem by being both fast and secure.
//                             ----------------------
//
//                               By Harry J. Smith

using System;
using System.IO;			// for StreamWriter I/O to a file
using System.Text;			// for Encoding I/O to a Text file
using System.Reflection;	// for Assembly, ...

namespace MyNamespace
{
	/// <summary>
	///             CrypC, a data encryption system, Console version
	///             Developed in TURBO Pascal 5.5 and 6.0, converted to C
	///             and now in Visual C# .Net
	///
	/// ---------------- *** LEGAL NOTICE: *** ------------------
	///
	/// This program is free software; you can redistribute it and/or modify
	/// it under the terms of the GNU General Public License (GPL) as published
	/// by the Free Software Foundation. cf. the file COPYING.txt.
	///
	/// ------------ *** end of legal notice *** ------------------
	/// </summary>
	
	class Cryp
	{
		// The main entry point for the application CrypC
		[STAThread]
		static int Main(string[] args)
		{
			try
			{
				Console.SetBufferSize(80, 1000);	// allow scrolling of output window
			}
			catch { }
			system = Environment.GetEnvironmentVariable("SYSTEM");
			title = "CrypC - A data encryption system written in C#";
			Version aVersion = Assembly.GetExecutingAssembly().GetName().Version;
			version = "C# Console Version " + aVersion.ToString();
			homeDir = new DirectoryInfo(Environment.CurrentDirectory).ToString();
			logg = false;
			quiet = true;
			quit = false;
			restore = false;
			nL = Environment.NewLine;
			arg0 = "";
			if (args.Length > 0)				// if arguments
				arg0 = args[0];					// save 1st argument
			Init();
			if (arg0 != "")						// if arguments
			{
				fileX = arg0;					// 1st arg is name of FileX
				arg0 = "";
				stIn = "Ex";
				WriteLn(nL + "1St Command line argument = " + fileX);
				GetCommand();
				DoCommand();
			}
			stIn = "";
			RunCryp();
			if (logg)
				ToggleLogg();
			return 0;
		} // Main

		// ------- initialize/restart
		private static void Init()
		{
			quiet = true;
			Directory.SetCurrentDirectory(homeDir);
			string author1 = "Copyright (c) 1981-2007 by author: Harry J. Smith,";
			string author2 = "19628 Via Monte Dr., Saratoga, CA 95070.  All rights reserved.";
			string usage1 = "Usage: CrypC [command-file-name-to-execute]";
			Console.Clear();
			WriteLn("");
			WriteLn(title);		// show credits
			WriteLn(version);
			WriteLn(author1);
			WriteLn(author2);
			WriteLn("");
			WriteLn(usage1);		// show usage
			WriteLn("");
			file1 = "File1.txt";	// name of File1
			file2 = "File2.txt";	// name of File2
			fileC = "FileC.txt";	// name of FileC
			fileD = "FileD.txt";	// name of FileD
			fileE = "FileE.txt";	// name of FileE
			fileK = "FileK.txt";	// name of FileK
			fileL = "FileL.CCL";	// name of FileL
			fileX = "FileX.CCC";	// name of FileX
			repeat2 = true;
			if (arg0 == "")
			{
				WriteLn("Help");
				Help();
				WriteLn("");
			}
			param = "1";				// input key for encryption
			// Key24 = 1///////////////////////
			WriteLn("Key = 1");
			GetKey();					// compute  keyStd
			if (arg0 == "")
			{
				WriteLn(nL + "State" + nL);
				State();
			}
		} // init

		public static void RunCryp()	// get instructions from operator
		{
			while (!quit)
			{
				if (stIn != null && stIn.Length > 1 && stIn.Substring(0, 2) == "//")
					WriteLn(""); // ??
				WriteLn("");
				stIn = Console.ReadLine();
				WriteLog(stIn + nL);
				GetCommand();
				DoCommand();
			}
		} // RunCryp

		public static void DoCommand()	// do a command
		{
			if (stIn == "")	// "" = NoOp
				return;
			if ((name == "CHDIR" || name == "CD") && param != "")
				ChangeDir();
			else if ((name == "CLEARLOG" || name == "CL") && param == "")
				ClearLog();
			else if ((name == "DECODE" || name == "DE") && param == "")
				Decode();
			else if ((name == "ENCODE" || name == "EN") && param == "")
				Encode();
			else if ((name == "EQUAL" || name == "EQ") && param == "")
				Equal(fileC, fileD);
			else if ((name == "EQUAL12" || name == "EQ12") && param == "")
				Equal(file1, file2);
			else if ((name == "EXECUTE" || name == "EX") && param == "")
				Execute();
			else if ((name == "EXIT" || name == "Q") && param == "")
				Exit();
			else if ((name == "FILE1" || name == "F1") && param != "")
				File1();
			else if ((name == "FILE2" || name == "F2") && param != "")
				File2();
			else if ((name == "FILEC" || name == "FC") && param != "")
				FileC();
			else if ((name == "FILED" || name == "FD") && param != "")
				FileD();
			else if ((name == "FILEE" || name == "FE") && param != "")
				FileE();
			else if ((name == "FILEK" || name == "FK") && param != "")
				FileK();
			else if ((name == "FILEL" || name == "FL") && param != "")
				FileL();
			else if ((name == "FILEX" || name == "FX") && param != "")
				FileX();
			else if ((name == "HELP" || name == "HE") && param == "")
				Help();
			else if ((name == "INIT" || name == "IN") && param == "")
				Init();
			else if ((name == "INITC" || name == "IC") && param == "")
				InitC();
			else if (name == "KEY" && param != "")
				GetKey();
			else if ((name == "KEYF" || name == "KF") && param == "")
				GetKeyF();
			else if (name == "LF" && param == "")
				LogF();
			else if (name == "LOG" && param != "")
				Log();
			else if (name == "LT" && param == "")
				LogT();
			else if (name == "QF" && param == "")
				QuietF();
			else if (name == "QT" && param == "")
				QuietT();
			else if (name == "QUIET" && param != "")
				Quiet();
			else if ((name == "RESTORE" || name == "RE") && param == "")
				Restore();
			else if ((name == "SAVE" || name == "SA") && param == "")
				Save();
			else if ((name == "STATE" || name == "ST") && param == "")
			{
				WriteLn("");
				State();
			}
			else if (stIn.Length > 1 && stIn.Substring(0, 2) == "//")
				return;
			else
				WriteLn("I do not understand '" + stIn + "'");
		} // DoCommand

		public static void GetCommand()	// get name and parameter in command string
		{
			stIn = stIn.Trim();	// trim all white space from start and end of command
			string st = stIn;
			int at = st.IndexOf("//");
			if (at >= 0)
			{
				st = st.Substring(0, at);
				st = st.Trim();			// trim again
			}
			at = st.IndexOf("=");
			if (at >= 0)
			{
				name = st.Substring(0, at);
				name = name.Trim();
				name = name.ToUpper();
				if (name == "KEY")
					st = stIn;
				int len = st.Length;
				param = st.Substring(at + 1, len - at - 1);
				param = param.Trim(' ');
				if (name != "KEY")
				{
					param = param.Trim(' ', '"');
					at = param.IndexOf('"');
					if (at >= 0)
					{
						param = param.Substring(0, at);
						param = param.Trim();			// trim again
					}
					if (param == "")
						name = "NoName";
				}
			}
			else
			{
				name = st.ToUpper();
				param = "";
			}
		} // GetCommand

		public static void ChangeDir()	// change the default disk directory
		{
			string stL = param;
			string savDir = "";
			try
			{
				savDir = Environment.CurrentDirectory.ToString();
				if (stL == savDir) stL = "";
				if (stL != "") Directory.SetCurrentDirectory(stL);
			}
			catch
			{
				// -------- Trap for error handling
				WriteLn("Path not found!");
			}
			finally
			{
				if (!quiet)
				{
					if (Environment.CurrentDirectory.ToString() != savDir)
					{
						WriteLn("Directory changed to \"" + Environment.CurrentDirectory.ToString() + '"');
					}
					else
					{
						WriteLn("Directory not changed \"" + Environment.CurrentDirectory.ToString() + '"');
					}
					WriteLn("Home directory is \"" + homeDir + '"');
				}
			}
		} // ChangeDir

		public static void ClearLog()	// clear Log file
		{
			DateTime dt = DateTime.Now;
			string dateTimeString = dt.ToString("MM/dd/yyyy hh:mm:ss tt");
			bool logS = logg; // save Log status in a local
			if (logg)
			{
				string st = "Log file \"" + fileL + "\" Closed " + dateTimeString;
				if (!quiet)
				{
					WriteLn(st);
					outL.WriteLine();
				}
				outL.WriteLine("");
				outL.Close();   // close the file
				logg = false;
			}
			// open log file for output as outL, clear the file, outL gotten by open routine
			if (OpenWriteSt(out outL, fileL))
			{
				logg = true;    // set to get message to the file
				string st = "Log file \"" + fileL + "\" Cleared " + dateTimeString;
				if (!quiet)
				{
					WriteLn(st);
					WCurDir(fileL);
				}
				else
				{
					outL.WriteLine(st);
				}
				outL.WriteLine(title + ", " + version);
				if (system != "") outL.WriteLine("Run on: " + system);
				if (!logS)
				{
					outL.Close(); // close the file
					logg = false;
				}
			}
		} // ClearLog

		public static void Decode()	// Decode FileE to FileD using key
		{
			if (fileE == fileD)
			{
				WriteLn("Cannot Decode file \"" + fileE + "\" to itself" + nL);
				return;
			}
			FileStream inF = null;
			BinaryReader bR = null;
			if (!OpenReadBr(out inF, out bR, fileE))	// Try to open FileE
			{
				return;
			}
			FileStream outF = null;
			BinaryWriter bW = null;
			if (!OpenWriteBw(out outF, out bW, fileD))	// Try to open FileD
			{
				inF.Close();
				return;
			}
			int nr;
			// Read first record of file
			if (!ReadFBr(inF, bR, buf, CONSIZE, out nr) || nr != CONSIZE)
			{
				WriteLn("Cannot read file \"" + fileE + '"' + nL);
				outF.Close();
				inF.Close();
				return;
			}
			DoIt(inF, bR, outF, bW);
			outF.Close();
			inF.Close();
		} // Decode

		public static void Encode()	// Encode FileC to FileE using key
		{
			if (fileC == fileE)
			{
				WriteLn("Cannot Encode file \"" + fileE + "\" to itself" + nL);
				return;
			}
			FileStream inF = null;
			BinaryReader bR = null;
			if (!OpenReadBr(out inF, out bR, fileC))	// Try to open FileC
			{
				return;
			}
			FileStream outF = null;
			BinaryWriter bW = null;
			if (!OpenWriteBw(out outF, out bW, fileE))	// Try to open FileE
			{
				inF.Close();
				return;
			}
			string savDir = Environment.CurrentDirectory.ToString();
			Directory.SetCurrentDirectory(homeDir);				// Cry.con is always in homeDir
			FileStream conFile = null;
			BinaryReader bC = null;
			if (!OpenReadBr(out conFile, out bC, "Cry.con"))	// Try to open continuation file
			{
				outF.Close();
				inF.Close();
				Directory.SetCurrentDirectory(savDir);
				return;
			}
			Directory.SetCurrentDirectory(savDir);
			int nr;
			// Read continuation file
			if (!ReadFBr(conFile, bC, buf, CONSIZE, out nr) || nr != CONSIZE)
			{
				WriteLn("Cannot read file \"Cry.con\"");
				conFile.Close();
				outF.Close();
				inF.Close();
				return;
			}
			conFile.Close();
			if (!WriteFBw(outF, bW, buf, nr))	// Write 1st record of fileE, if write error, exit
			{
				outF.Close();
				inF.Close();
				return;
			}
			DoIt(inF, bR, outF, bW);
			outF.Close();
			inF.Close();
			Term();			// Terminate processing, update Cry.con
		} // Encode

		public static void Equal(string fileA, string fileB)	// does the contents of fileA == fileB
		{
			string savDir = Environment.CurrentDirectory.ToString();
			if (savDir != homeDir)
				Directory.SetCurrentDirectory(homeDir);
			if (fileA == fileB && savDir == homeDir)
			{
				WriteLn("True, files \"" + fileA + "\" and \"" + fileB + "\" are the same file");
				return;
			}
			FileStream aFile = null;
			BinaryReader bA = null;
			if (!OpenReadBr(out aFile, out bA, fileA, true))  // Try to open FileA in homeDir only
			{
				if (savDir != homeDir)
					Directory.SetCurrentDirectory(savDir);
				return;
			}
			if (savDir != homeDir)
				Directory.SetCurrentDirectory(savDir);
			FileStream bFile = null;
			BinaryReader bB = null;
			if (!OpenReadBr(out bFile, out bB, fileB, true))  // Try to open FileB in CurrentDirectory first
			{
				aFile.Close();
				return;
			}
			if (aFile.Name == bFile.Name)
			{
				WriteLn("True, files \"" + fileA + "\" and \"" + fileB + "\" are the same file");
				aFile.Close();
				bFile.Close();
				return;
			}
			if (aFile.Length != bFile.Length)
			{
				WriteLn("False, files \"" + fileA + "\" and \"" + fileB + "\" are not the same length");
				aFile.Close();
				bFile.Close();
				return;
			}
			int i;
			int nA = -1;  // Number of bytes read from FileA
			int nB = -1;  // Number of bytes read from FileB
			while (nA != 0)
			{
				bool dOK = ReadFBr(aFile, bA, buf, BUFSIZE, out nA) && nA != 0;
				bool cOK = ReadFBr(bFile, bB, buf2, BUFSIZE, out nB) && nB != 0;
				if (dOK != cOK || nA != nB)
				{
					WriteLn("False, files \"" + fileA + "\" and \"" + fileB + "\" are not the same length");
					aFile.Close();
					bFile.Close();
					return;
				}
				if (dOK && cOK)
				{
					for (i = 0; i < (nA + 1) / 2; i++)
					{
						if (buf[i] != buf2[i])
						{
							WriteLn("False, files \"" + fileA + "\" and \"" + fileB + "\" are not equal");
							return;
						}
					}
				}
			}
			WriteLn("True, files \"" + fileA + "\" and \"" + fileB + "\" are equal");
			aFile.Close();
			bFile.Close();
		} // Equal

		public static void DoIt(FileStream inF, BinaryReader bR, FileStream outF, BinaryWriter bW)
		// This is the heart of the encryption method
		{
			int i;
			for (i = 0; i < CONSIZE / 2; i++)
				pool[i] = (Int16)buf[i];
			for (i = 0; i < 9; ++i)			// Save and encode continuation seeds
			{
				rC[i] = pool[i];
				rI[i] = (Int16)(rS[i] ^ pool[i]);
				if (rI[i] == 0)				// Do not allow a zero seed
					rI[i] = 1;
			}
			rR = Rc9();						// Init first word of running
			next = 0;
			for (i = 0; i < POOLSIZE; ++i)	// Initialize pool, random int
			{
				pool[i] = rR += Rn(next);
				if (++next == 9)
					next = 0;
			}
			rR = Rc9();				// Init rR for first random index into pool
//			for (i = 0; i < 9; ++i)	// diag
//				WriteLn("rS[" + i + "] = " + rS[i]);
//			for (i = 0; i < 9; ++i)
//				WriteLn("rI[" + i + "] = " + rI[i]);
			int ind;		// Random index into pool of random integers
			int nr = -1;	// Number of bytes read into buf
			while (ReadFBr(inF, bR, buf, BUFSIZE, out nr) && nr != 0)
			{
				for (i = 0; i < (nr + 1) / 2; i++)
				{
					ind = rR & poolMask;			// Random index to random pool
					buf[i] = (UInt16)(buf[i] ^ pool[ind]);	// XOR data with random int
					pool[ind] ^= ( rR += Rn(next));	// XOR random int with next rR
					if( ++next == 9)				// Cycle to next generator
						next = 0;
				}
				if (!WriteFBw(outF, bW, buf, nr))	// Write output file, if write error, exit
					return;
			}
		} // DoIt

		public static void Term()	// Terminate processing, update Cry.con
		{
			string savDir = Environment.CurrentDirectory.ToString();
			Directory.SetCurrentDirectory(homeDir);
			FileStream conFile = null;
			BinaryWriter bW = null;
			if (OpenWriteBw(out conFile, out bW, "Cry.con"))
			{
				int i;
				for (i = 0; i < 9; ++i)       // Restore clear continuation seeds
					rI[i] = rC[i];
				for (i = POOLSIZE - 1; i >= 0; i--)	// Build a random record
					pool[i] = Rc9();
				for (i = 0; i < CONSIZE / 2; i++)   // copy to buf
					buf[i] = (UInt16)pool[i];
				// Write continuation file
				if (WriteFBw(conFile, bW, buf, CONSIZE))
					if (!quiet)
						WriteLn("Continuation file \"Cry.con\" updated");
				conFile.Close();
			}
			Directory.SetCurrentDirectory(savDir);
		} // Term

		public static void Execute()	// Execute command file FileX
		{
			stIn = "";
			StreamReader inF = null;
			if (!OpenReadSt(out inF, fileX))
				return;
			stIn = inF.ReadLine();		// get first line to execute
			while (stIn != null)		// loop until end of file
			{
				WriteLn("");
				if (stIn != "")
				{
					WriteLn(stIn);
					GetCommand();
					DoCommand();
				}
				stIn = inF.ReadLine();	// get the next line to execute
			}
			inF.Close();
		} // Execute

		public static void Exit()	// Exit/Quit the program
		{
			quit = true;
		} // Exit

		public static void File1()	// Set Input file name of file1 for Equal12 command
		{
			file1 = param;	// name of File1
			if (!restore)
				VerifyF();
		} // File1

		public static void File2()	// Set Input file name of file2 for Equal12 command
		{
			file2 = param;	// name of File2
			if (!restore)
				VerifyF();
		} // File2

		public static void FileC()	// Set Input file name of clear file to be encrypted
		{
			fileC = param;	// name of FileC
			if (!restore)
				VerifyF();
		} // FileC

		public static void FileD()	// Set Output file name of file to decrypt to
		{
			fileD = param;	// name of FileD
			if (!restore)
				VerifyF();
		} // FileD

		public static void FileE()	// Set Output file name of file to encrypt to
		{
			fileE = param;	// name of FileE
			if (!restore)
				VerifyF();
		} // FileE

		public static void FileK()	// Set file name of key file
		{
			fileK = param;	// name of FileK
			if (!restore)
				VerifyF();
		} // FileK

		public static void FileL()	// Set file name of Log file
		{
			fileL = param;	// name of FileL
			if (!restore)
				VerifyF();
		} // FileL

		public static void FileX()	// Set file name of eXecute file
		{
			fileX = param;	// name of FileX
			if (!restore)
				VerifyF();
		} // FileX

		public static void VerifyF()	// verify that File param exists
		{
			FileStream inF = null;
			BinaryReader bR = null;
			if (OpenReadBr(out inF, out bR, param))    // Try to open new File param
			{
				inF.Close();
				if (!quiet)
					WriteLn("File \"" + param + "\" verified and closed");
			}
		} // VerifyF

		public static void Help()	// Show Help file CrypCHelp.txt
		{
			string st = "";
			StreamReader inF = null;
			if (!OpenReadSt(out inF, "CrypCHelp.txt"))
				return;
			WriteLn("");
			st = inF.ReadLine();	// get the next line of Help
			while (st != null)	// loop until end of file
			{
				WriteLn(st);
				st = inF.ReadLine();	// get the next line of Help
			}
			inF.Close();
		} // Help

		public static void InitC()	// Initialize the Continuation File Cry.con
		{
			string savDir = Environment.CurrentDirectory.ToString();
			Directory.SetCurrentDirectory(homeDir);
			FileStream conFile = null;
			BinaryWriter bW = null;
			if (OpenWriteBw(out conFile, out bW, "Cry.con"))
			{
				int i;
				for (i = 0; i < 9; i++)				// Initialize Cry.con file
					rI[i] = rS[i];
				for (i = POOLSIZE - 1; i >= 0; i--)	// Build a random record
					pool[i] = Rc9();
				for (i = 0; i < CONSIZE / 2; i++)	// copy to buf
					buf[i] = (UInt16)pool[i];
				// Write continuation file
				if (WriteFBw(conFile, bW, buf, CONSIZE))
					if (!quiet)
						WriteLn("Continuation file \"Cry.con\" updated");
				conFile.Close();
			}
			Directory.SetCurrentDirectory(savDir);
		} // InitC

		public static void GetKey()	// Input a key from keyboard
		{
			keyIn = param;
			StdKey();
		} // GetKey

		public static void GetKeyF()	// Input a key from FileK
		{
			StreamReader inF = null;
			if (!OpenReadSt(out inF, fileK))	// Try to open key file
				return;
			keyIn = inF.ReadLine().Trim();		// get the first line of fileK
			inF.Close();
			WriteLn("Key = " + keyIn);
			StdKey();
		}
		// GetKeyF

		public static void StdKey()	// Standardize keyIn
		{
			int i, j;	// Utility indexes
			char ch;	// Utility character
			string key;	// Temp value of key
			keyStd = keyIn;
			Standard(ref keyStd);
			key = keyStd;
			int len = keyIn.Length;
			if (len > MAXKEY)
			{
				for (i = 1; i < MAXKEY; i++)
				{
					ch = (char)((int)(key[i]) + (int)(key[i - 1]));
					SetSt(ref key, i, ch);
				}
				j = len - 1;
				for (i = 25; i < len; i++)
				{
					j = j - 1;
					ch = (char)((int)(key[j]) + (int)(key[j + 1]));
					SetSt(ref key, j, ch);
				}
			}
			key24 = "";
			for (i = 0; i < MAXKEY; i++)
				key24 += (char)(0);
			j = 0;
			for (i = 0; i < len; i++)
			{
				ch = (char)((int)(key24[j]) + (int)(key[i]));
				SetSt(ref key24, j, ch);
				Math.DivRem(j + 1, MAXKEY, out j);
			}
			for (i = 0; i < MAXKEY; i++)
				if (key24[i] == (char)(0))
					SetSt(ref key24, i, ' ');
			Standard(ref key24);
			ConvKey();
			WriteLn(keyStd + " = input key in standard form");
			WriteLn(key24 + " = 24-character key in standard form");
		} // StdKey

		public static void ConvKey()	// Convert the encryption key to 9 seeds
		// Convert the encryption key to 9 seeds
		//
		// input: key24[i], i = 0, ..., 23
		// output:   rS[j], j = 0, ..., 8
		//
		//   [0]    [1]    [2]    [3]    [4]    [5]    [6]   ...  [22]   [23]
		// 111111 111111 111111 111111 111111 111111 111111      111111 111111
		//    6      6     4  2    6      6    2  4     6       6      6
		// ****** seed0 *****...... seed1 ......***** seed2  ...  seed8 ******
		//       16 BITS           16 BITS       16 BITS     16 BITS
		//
		// 144 bit key converted to 144 bits of seed,
		// seeds of zero are changed to 1.
		{
			int  i, j;
			int[] keyI = new int[MAXKEY];	// Key in integer form, 6 bits each int
			for(i = 0; i < MAXKEY; i++)
				keyI[i] = key24[i] & 63;
			for(i = 0, j = 0; j < 9; i += 8, j += 3)
			{
				rS[j] =	(Int16)((keyI[i]   << 10) |
						(keyI[i+1] <<  4) |
						(keyI[i+2] >>  2));

				rS[j + 1] = (Int16)((keyI[i + 2] << 14) |
							 (keyI[i + 3] << 8) |
							 (keyI[i + 4] << 2) |
							 (keyI[i + 5] >> 4));

				rS[j + 2] = (Int16)((keyI[i + 5] << 12) |
							 (keyI[i + 6] << 6) |
							  keyI[i + 7]);
			}
			for(j = 0; j < 9; j++)		// Do not allow a zero seed
				if(rS[j] == 0)
					rS[j] = 1;
			repeat2 = true;
		} // ConvKey

		public static void Standard(ref string key)	// Standardize a key string
		// Output characters are from 33 to 95 inclusive
		{
			char ch;
			int len = key.Length;
			for (int i = 0; i < len; i++)
			{
				ch = key[i];
				ch &= (char)127;
				if (ch > 95)
					ch -= (char)32;
				if (ch < 32)
					ch += (char)32;
				if (ch == ' ')
					ch = '/';
				SetSt(ref key, i, ch);	// key[i] = ch;
			}
		} // Standard

		public static void Log()	// Set Log screen to log file mode on or off
		{
			bool loggL = param != "0";
			if (logg != loggL)
				ToggleLogg();
		} // Log

		public static void LogT()	// Set Log screen to log file mode on (True)
		{
			if (!logg)
				ToggleLogg();
		} // LogT

		public static void LogF()	// Set Log screen to log file mode off (False)
		{
			if (logg)
				ToggleLogg();
		} // LogF

		public static void Quiet()	// Set Quiet mode on or off
		{
			quiet = param != "0";
		} // Quiet

		public static void QuietT()	// Set Quiet mode on (True)
		{
			quiet = true;
		} // QuietT

		public static void QuietF()	// Set Quiet mode off (False)
		{
			quiet = false;
		} // QuietF

		public static void Restore()	// Restore the state of inputs from Save.CCC
		{
			StreamReader inF = null;
			if (!OpenReadSt(out inF, "Save.CCC"))
				return;
			restore = true;
			stIn = inF.ReadLine();	// get the next line of Save.CCC
			while (stIn != null)	// loop until end of file
			{
				GetCommand();
				DoCommand();
				stIn = inF.ReadLine();	// get the next line of Save.CCC
			}
			inF.Close();
			WriteLn("");
			State();
			restore = false; 
		} // Restore

		public static void Save()	// Save the state of inputs to Save.CCC
		{
			string stL = "0";
			if (logg)
				stL = "1";
			string stQ = "0";
			if (quiet)
				stQ = "1";
			StreamWriter outF = null;
			if (!OpenWriteSt(out outF, "Save.CCC"))
				return;
			if (!WriteFSt(outF, "ChDir = " + Environment.CurrentDirectory + nL))
			{
				outF.Close();
				return;
			}
			WriteFSt(outF, "File1 = " + QuoteFN(file1) + nL);
			WriteFSt(outF, "File2 = " + QuoteFN(file2) + nL);
			WriteFSt(outF, "FileC = " + QuoteFN(fileC) + nL);
			WriteFSt(outF, "FileD = " + QuoteFN(fileD) + nL);
			WriteFSt(outF, "FileE = " + QuoteFN(fileE) + nL);
			WriteFSt(outF, "FileK = " + QuoteFN(fileK) + nL);
			WriteFSt(outF, "FileL = " + QuoteFN(fileL) + nL);
			WriteFSt(outF, "FileX = " + QuoteFN(fileX) + nL);
			WriteFSt(outF, "Key = " + keyIn + nL);
			WriteFSt(outF, "Log = " + stL + nL);
			WriteFSt(outF, "Quiet = " + stQ + nL);
			outF.Close();
			if (!quiet)
				WriteLn("Save file \"Save.CCC\" updated");
		} // Save

		public static string QuoteFN(string fileName)	// quote fileName if needed
		{
			string delim = ";=/:()@\""; // to be consistent with CrypF
			if (fileName.Contains(" "))
				return '"' + fileName + '"';
			for (int i = 0; i < delim.Length; i++)
			{
				if (fileName.Contains(delim[i].ToString()))
					return '"' + fileName + '"';
			}
			return fileName;
		} // QuoteFN

		public static void State()	// Show state of inputs
		{
			string stL = "0";
			if (logg)
				stL = "1";
			string stQ = "0";
			if (quiet)
				stQ = "1";
			WriteLn(" Home Dir: \"" + homeDir + '"');
			WriteLn("File path: \"" + Environment.CurrentDirectory.ToString() + '"');
			WriteLn("");
			WriteLn(" File1 = " + QuoteFN(file1));
			WriteLn(" File2 = " + QuoteFN(file2));
			WriteLn(" FileC = " + QuoteFN(fileC));
			WriteLn(" FileD = " + QuoteFN(fileD));
			WriteLn(" FileE = " + QuoteFN(fileE));
			WriteLn(" FileK = " + QuoteFN(fileK));
			WriteLn(" FileL = " + QuoteFN(fileL));
			WriteLn(" FileX = " + QuoteFN(fileX));
			WriteLn(" KeyIn = " + keyIn);
			WriteLn("KeyStd = " + keyStd);
			WriteLn(" Key24 = " + key24);
			WriteLn("   Log = " + stL);
			WriteLn(" Quiet = " + stQ);
		} // State

		public static void ToggleLogg() //Toggle Logging of output screen to Log File
		{
			DateTime dt = DateTime.Now;
			string dateTimeString = dt.ToString("MM/dd/yyyy hh:mm:ss tt");
			if (logg)
			{
				string st = "Log file \"" + fileL + "\" Closed " + dateTimeString;
				if (!quiet)
				{
					WriteLn(st);
					outL.WriteLine();
				}
				outL.Close(); // close the file
				logg = false;
			}
			else
			{
				bool check = OpenAppend(ref outL, fileL);
				if (check) // open fileL for Append as outL
				{
					logg = true;
					string st = "Log file \"" + fileL + "\" Opened for Append " + dateTimeString;
					if (!quiet)
					{
						WriteLn(st);
						WCurDir(fileL);
						outL.WriteLine(title + ", " + version);
						if (system != "") outL.WriteLine("Run on: " + system);
					}
				}
				else
				{
					return;
				}
			}
		} // ToggleLogg

		public static void WriteLn(string st) // Write st plus linefeed
		{
			Write(st + nL);
		}

		public static void Write(string st) // print string to console and Log file
		{
			Console.Write(st);
			WriteLog(st);
		} // Write

		public static void WriteLog(string st) // print string to Log file
		{
			if (logg)
			{
				try { outL.Write(st); }
				catch { }
			}
		} // WriteLog

		public static bool OpenReadBr(out FileStream inF, out BinaryReader bR, string fName)
		// open file for Reading in CurrentDirect or homeDir
		{
			return OpenReadBr(out inF, out bR, fName, false);
		} // OpenReadBr

		public static bool OpenReadBr(out FileStream inF, out BinaryReader bR, string fName, bool currOnly)
		// open file for Reading in CurrentDirect or homeDir
		{
			string stEx1;
			string stEx2 = "";
			string savDir = Environment.CurrentDirectory.ToString();
			inF = null; // inF must be assigned to before control leaves the current method
			bR = null;  // bR must be assigned to before control leaves the current method
			if (OpenReadFileBr(out inF, out bR, fName, out stEx1))
			{
				if (!quiet)
				{
					WriteLn("File \"" + fName + "\" opened for reading");
					WCurDir(fName);
				}
				return true;
			}
			if (savDir != homeDir && !currOnly)
				if (savDir != homeDir)
				{
					Directory.SetCurrentDirectory(homeDir);
					if (OpenReadFileBr(out inF, out bR, fName, out stEx2))
					{
						if (!quiet)
						{
							WriteLn("File \"" + fName + "\" opened for reading");
							WCurDir(fName);
						}
						Directory.SetCurrentDirectory(savDir);
						return true;
					}
					Directory.SetCurrentDirectory(savDir);
				}
			WriteLn("Cannot open file \"" + fName + "\" for input");
			WriteLn("Not in current directory \"" + savDir + '"');
			WriteLn("OpenRead: " + stEx1);		// file not found most likely
			if (savDir != homeDir && !currOnly)
			{
				WriteLn("Not in home directory \"" + homeDir + '"');
				WriteLn("OpenRead: " + stEx2);	// file not found most likely
			}
			return false;
		} // OpenReadBr

		public static bool OpenReadFileBr(out FileStream inF, out BinaryReader bR, string fName, out string stEx)
		// open file for Reading from CurrentDirectory
		{
			inF = null; // inF must be assigned to before control leaves the current method
			bR = null;  // bR must be assigned to before control leaves the current method
			stEx = "";
			try
			{
				inF = new FileStream(fName, FileMode.Open); // open fName for input as inF
				bR = new BinaryReader(inF);
				return true;
			}
			catch (Exception ex)
			{
				stEx = ex.Message.ToString();
				return false;
			}
		} // OpenReadFileBr

		public static void WCurDir(string fName)
		// write current disk directory with final \
		// using Environment.CurrentDirectory.ToString(): C:\ already has the \ but non-root directories do not
		{
			// this can garble some output, but good info
			string cD = Directory.GetCurrentDirectory();
			if (cD.Substring(cD.Length - 1) != @"\") cD += @"\";
			if (fName.Length > 1 && fName[1] == ':')
			{
				WriteLn("Directory name = \"" + cD + '"');
			}
			else
			{
				WriteLn("Full name = \"" + cD + fName + '"');
			}
		}

		public static bool OpenWriteBw(out FileStream outF, out BinaryWriter bW, string fName)
		// open file for Writing to CurrentDirect
		{
			outF = null;	// outF must be assigned to before control leaves the current method
			bW = null;		// bW must be assigned to before control leaves the current method
			try
			{
				if (File.Exists(fName))
					File.Delete(fName);
				// open fName for output as outF
				outF = new FileStream(fName, FileMode.CreateNew);	// Appending = false
				bW = new BinaryWriter(outF);
				if (!quiet)
				{
					WriteLn("File \"" + fName + "\" opened for writing");
					WCurDir(fName);
				}
				return true;
			}
			catch (Exception ex)
			{
				WriteLn("Cannot open file \"" + fName + "\" for output");
				WriteLn(ex.Message + nL);
				return false;
			}
		} // OpenWriteBw

		public static bool OpenWriteSt(out StreamWriter outF, string fName)
		// open file for Writing to homeDir
		{
			string savDir = Environment.CurrentDirectory.ToString();
			Directory.SetCurrentDirectory(homeDir);
			outF = null;	// outF must be assigned to before control leaves the current method
			try
			{
				// open fName for output as outF
				outF = new StreamWriter(fName, false, Encoding.ASCII);	// Appending = false
				if (!quiet)
				{
					WriteLn("File \"" + fName + "\" opened for writing");
					WCurDir(fName);
				}
				Directory.SetCurrentDirectory(savDir);
				return true;
			}
			catch (Exception ex)
			{
				WriteLn("Cannot open file \"" + fName + "\" for output");
				WriteLn("OpenWrite: " + ex.Message + nL);
				Directory.SetCurrentDirectory(savDir);
				return false;
			}
		} // OpenWriteSt

		public static bool OpenAppend(ref StreamWriter outF, string fName)  // open file for Writing
		{
			try
			{
				// open fName for Append as outF
				outF = new StreamWriter(fName, true, Encoding.ASCII);   //Appending = true
				return true;
			}
			catch (Exception ex)
			{
				WriteLn("Cannot open file \"" + fName + "\" for output");
				WCurDir(fName);
				WriteLn("OpenAppend: " + ex.Message);
				return false;
			}
		}

		public static bool WriteFBw(FileStream outF, BinaryWriter bW, UInt16[] buf, int n)
		// write n bytes from buf to an opened file
		{
			if (buf.Length == 0 || n == 0)
				return true;
			if (outF == null || bW == null)
				return false;
			if (n > 2 * buf.Length)
				n = 2 * buf.Length;
			byte b;
			try
			{
				for (int i = 0; i < n; i++)
				{
					if ((i & 1) == 0)
						b = (byte)buf[i / 2];
					else
						b = (byte)(buf[i / 2] >> 8);
					bW.Write(b);
				}
				return true;
			}
			catch (Exception ex)
			{
				WriteLn("File write error " + ex.Message + nL);
				return false;
			}
		} // WriteFBw

		public static bool ReadFBr(FileStream inF, BinaryReader bR, UInt16[] buf, int n, out int nr)
		// read upto n bytes from an opened file to buf
		{
			nr = 0;
			if (buf.Length == 0 || n == 0)
				return false;
			if (inF == null || bR == null)
				return false;
			if (n > 2 * buf.Length)
				n = 2 * buf.Length;
			nr = 0;
			byte b;
			try
			{
				for (int i = 0; i < n; i++)
				{
					b = bR.ReadByte();
					if ((i & 1) == 0)
						buf[i / 2] = b;
					else
						buf[i / 2] = (UInt16)(buf[i / 2] + (b << 8));
					nr++;
				}
				return true;
			}
			catch { return true; }
		} // ReadFBr

		public static bool WriteFSt(StreamWriter outF, string st)	// write string to an opened file
		{
			if (st == "")
				return false;
			if (outF == null)
				return false;
			try
			{
				outF.Write(st);
				return true;
			}
			catch (Exception ex)
			{
				WriteLn("File write error " + ex.Message + nL);
				return false;
			}
		} // WriteFSt

		public static bool OpenReadSt(out StreamReader inF, string fName)
		// open file for Reading from CurrentDirectory or homeDir
		{
			string stEx1;
			string stEx2 = "";
			string savDir = Environment.CurrentDirectory.ToString();
			inF = null;
			if (OpenReadFileSt(out inF, fName, out stEx1))
			{
				if (!quiet)
				{
					WriteLn("File \"" + fName + "\" opened for reading");
					WCurDir(fName);
				}
				return true;
			}
			if (savDir != homeDir)
			{
				Directory.SetCurrentDirectory(homeDir);
				if (OpenReadFileSt(out inF, fName, out stEx2))
				{
					if (!quiet)
					{
						WriteLn("File \"" + fName + "\" opened for reading");
						WCurDir(fName);
					}
					Directory.SetCurrentDirectory(savDir);
					return true;
				}
				Directory.SetCurrentDirectory(savDir);
			}
			WriteLn("Cannot open file \"" + fName + "\" for input");
			WriteLn("Not in current directory " + savDir);
			WriteLn("OpenRead: " + stEx1);	// file not found most likely
			if (savDir != homeDir)
			{
				WriteLn("Not in home directory " + homeDir);
				WriteLn("OpenRead: " + stEx2);	// file not found most likely
			}
			return false;
		} // OpenReadSt

		public static bool OpenReadFileSt(out StreamReader inF, string fName, out string stEx)
		// open file for Reading from CurrentDirectory
		{
			inF = null;
			stEx = "";
			try
			{
				inF = new StreamReader(fName); // open fName for input as inF
				return true;
			}
			catch (Exception ex)
			{
				stEx = ex.Message.ToString();
				return false;
			}
		} // OpenReadFileSt

		public static void SetSt(ref string st, int i, char ch)	// set st[i] = ch
		{
			int len = st.Length;
			if (i < 0 || i >= len)
				return;
			st = st.Substring(0, i) + ch + st.Substring(i + 1, len - i - 1);
		} // SetSt

		public static Int16 Rn(int n)	// Returns the next random integer from rI[n], 0 <= n <= 8
		{
			int i;
			switch(n)
			{
			case 0:
				// 1st Congruential Generator, 16 bits
				// Generates Random integers from -32768 to 32767 inclusive
				// Cycle length = 65536 = 2**16
				//
				return (rI[0] = (Int16)(25173 * rI[0] + 6925));
			case 1:
				//  1st Shift-register Generator, 16 bits
				//  Random integer from -32768 to 32767 inclusive, 0 not generated
				//  Generator = -22620, Cycle length = 65535 = 3 * 5 * 17 * 257
				//
				if((rI[1] & 1) == 1)	// if odd
					return (rI[1] = (Int16)(((UInt16)rI[1] >> 1) ^ -22620));
				return (rI[1] = (Int16)((UInt16)rI[1] >> 1));
			case 2:
				//  2nd Congruential Generator, 16 bits
				//  Random integer from -32768 to 32767 inclusive
				//  Cycle length = 65537 = prime, zero repeats once
				//
				if (rI[2] == 0)
				{
					if (repeat2)
					{
						repeat2 = false;
						return 0;
					}
					else repeat2 = true;
				}
				return (rI[2] = (Int16)(23629 * rI[2] + 13849));
			case 3:
				//  2nd Shift-register Generator, 16 bits
				//  Random integer from -32768 to 32767, not all generated
				//  Generator = -07493, Cycle length = 65521 = prime, (65536 - 15)
				//
				if((rI[3] & 1) == 1)	// if odd
					rI[3] = (Int16)(((UInt16)rI[3] >> 1) ^ -7493);
				else
					rI[3] = (Int16)((UInt16)rI[3] >> 1);
				if (rI[3] == 1)
					for (i = 0; i < 14; i++)
						Rn(3);			// Throw 14 away
				return rI[3];
			case 4:
				//  3rd Congruential Generator, 16 bits
				//  Random integer from -32768 to 32767, not all generated
				//  Cycle length = 65519 = prime, (65536 - 17)
				//
				rI[4] = (Int16)(4821 * rI[4] + 13001);
				if (rI[4] == 0)
					for (i = 0; i < 17; i++)
						Rn(4);			// Throw 17 away
				return rI[4];
			case 5:
				//  3rd Shift-register Generator, 16 bits
				//  Random integer from -32768 to 32767, not all generated
				//  Generator = -25501, Cycle length = 65497 = prime, (65536 - 39)
				//
				if ((rI[5] & 1) == 1)
					rI[5] = (Int16)(((UInt16)rI[5] >> 1) ^ -25501);
				else
					rI[5] = (Int16)((UInt16)rI[5] >> 1);
				if (rI[5] == 1)
					for (i = 0; i < 38; i++)
						Rn(5);			// Throw 38 away
				return rI[5];
			case 6:
				//  4th Congruential Generator, 16 bits
				//  Random integer from -32768 to 32767, not all generated
				//  Cycle length = 65479 = prime, (65536 - 57)
				//
				rI[6] = (Int16)(10349 * rI[6] + 7001);
				if (rI[6] == 0)
					for (i = 0; i < 57; i++)
						Rn(6);			// Throw 57 away
				return rI[6];
			case 7:
				//  4th Shift-register Generator, 16 bits
				//  Random integer from -32768 to 32767, not all generated
				//  Generator = -18916, Cycle length = 65449 = prime, (65536 - 87)
				//
				if ((rI[7] & 1) == 1)
					rI[7] = (Int16)(((UInt16)rI[7] >> 1) ^ -18916);
				else
					rI[7] = (Int16)((UInt16)rI[7] >> 1);
				if (rI[7] == 1)
					for (i = 0; i < 86; i++)
						Rn(7);			// Throw 86 away
				return rI[7];
			case 8:
				//  5th Congruential Generator, 16 bits
				//  Random integer from -32768 to 32767, not all generated
				//  Cycle length = 65447 = prime, (65536 - 89)
				//
				rI[8] = (Int16)(30133 * rI[8] + 14001);
				if(rI[8] == 0)
					for (i = 0; i < 89; i++)
						Rn(8);			// Throw 89 away
				return rI[8];
			}
			return 0;					// This line is never reached
		} // Rn

		public static Int16 Rc9()		// Combination of Rn(0) thru Rn(8), one each
		{
			int i;
			Int16 rC = 0;
			for (i = 0; i < 9; i++)
				rC += Rn(i);
			return rC;
		} // Rc9

		// constants
		private static int BUFSIZE = 16384;	// Number of characters/bytes in file i/o buffer
		private static int CONSIZE = 18;	// Number of characters/bytes in Cry.con file
		private static int MAXKEY   = 24;	// Number of characters/bytes in short key
		private static int POOLSIZE = 2048;	// Number of 16-bit integers in random number pool

		private static bool logg;		// true if Logging screen output to FILE
		private static bool quiet;		// Quiet mode on (True) or off (False)
		private static bool quit;		// Exit/Quit flag
		private static bool repeat2;	// repeat flag used in Rn(2)
		private static bool restore;	// Restore flag
		private static int next;		// Next random no. routine to use, cycles 0 thru 8
		private static int poolMask = POOLSIZE - 1;	// Mask for index to random pool = 2**N - 1
		private static Int16 rR;		// Random running combination of rI[0] thru rI[8]
		private static Int16[] pool = new Int16[POOLSIZE];	// Pool of POOLSIZE random integers
		private static Int16[] rC = new Int16[9];	// Current value of clear continuation seeds
		private static Int16[] rI = new Int16[9];	// Current random integers from the 9 generators
		private static Int16[] rS = new Int16[9];	// Initial random seeds for the 9 generators
		private static StreamWriter outL;			// log file output #
		private static string arg0;	    // 1st command line argument
		private static string file1;	// Input file name of file1 for Equal12 command
		private static string file2;	// Input file name of file2 for Equal12 command
		private static string fileC;	// name of FileC, clear file to be encrypted
		private static string fileD;	// name of FileD, file to decrypt to
		private static string fileE;	// name of FileE, file to encrypt to or decrypt
		private static string fileK;	// name of FileK, key file
		private static string fileL;	// name of FileL, Log file
		private static string fileX;	// name of FileX, eXecute file
		private static string homeDir;	// initial disk directory when program started
		private static string key24;	// 24-character key in standard form
		private static string keyIn;	// input key for encryption
		private static string keyStd;	// input key in standard form
		private static string name;		// name in command string
		private static string nL;		// one Environment.NewLine \n or \r or \r\n
		private static string param;	// parameter in command string
		private static string stIn;		// command string as input
		private static string system;	// EnvironmentVariable("SYSTEM")
		private static string title;	// program title
		private static string version;	// = "C# Console Version x.x.x.xxxxx"
		private static UInt16[] buf = new UInt16[BUFSIZE / 2];	// Buffer for file i/o
		private static UInt16[] buf2 = new UInt16[BUFSIZE / 2];	// Buffer for file i/o
	}
}
