PROGRAM CodeBreaker;
{Programmer:	Gregory A. Perkins
				CS 65
		Due:	12 December 1996}
		
{This program accepts the name of a coded file, the number of possible key values in the 
	polyalphabetic cypher used, and at least one "known" string within the coded text and
	decodes the encrypted file and displays it to the user}
	
USES cs65;

{Declarations Module}
CONST MessMax = 255;
	  CharRange = 95;
	  TopCharCode = 126;
	  KeySize = 10;

TYPE Message = ARRAY[1..MessMax] OF CHAR;
	 KeyCode = ARRAY[1..KeySize] OF INTEGER;

VAR TestKey, i, Count, NumKeysNeeded, NumKeysFound : INTEGER;
	CharCycle, MessPointer, NumChecks : INTEGER;
	Found : BOOLEAN;
	FileName, SearchString : STRING;
	InFile : TEXT;
	Mess, MessAnswer : Message;
	Key : KeyCode;
	
PROCEDURE Decrypt(Shift : INTEGER; VAR Code : CHAR);
VAR NumCode : INTEGER;
BEGIN
	NumCode := ORD(Code) + Shift;
	IF NumCode > TopCharCode THEN
		NumCode := NumCode - CharRange;
	Code := CHR(NumCode);
END;

BEGIN  {program}

{Needed information prompt module}
WRITE('Enter the name of the file you would like to read : ');
READLN(FileName);
WRITE('Enter the number of encryption keys searching for : ');
READLN(NumKeysNeeded);
WRITE('Enter a known "string" in the encrypted text : ');
READLN(SearchString);

{Read File Module}
RESET(InFile, FileName);
i := 0;
WHILE NOT EOF(InFile) DO
	BEGIN
		i := i + 1;
		READ(Infile, Mess[i]);
	END;  {while loop}
CLOSE(Infile);

{Initialization module}
TestKey := 1;  {Current "key"/"offset" value used for decryption}
NumKeysFound := 0;  {Number of "keys" found so far}

REPEAT		
	{Status Display Module}
	WRITE('Testing Key Number : ', TestKey:1,'.');
	WRITELN('  Found ', NumKeysFound:1, ' code keys.');
	
	{Decrypt File Module}
	FOR Count := 1 TO i DO
		Decrypt(1, Mess[Count]);
			
	{Searching module}
	FOR CharCycle := 1 TO NumKeysNeeded DO
		BEGIN
			Found := FALSE;  {"Found" is true when there is a possible answer found}
			MessPointer := 0;
			REPEAT  {compare every char in message to char at CharCycle in SearchString}
				MessPointer := MessPointer + 1;  {point to next char in the message}
				IF (Mess[MessPointer]) = (SearchString[CharCycle]) THEN
					BEGIN
						Found := TRUE;  {set Found to true until proved otherwise}
						FOR NumChecks := 1 TO (((LENGTH(SearchString)) DIV NumKeysNeeded) - 1) DO
							IF (Mess[MessPointer + (NumKeysNeeded * NumChecks)] <> SearchString[CharCycle + (NumKeysNeeded * NumChecks)]) THEN
									Found := FALSE;  {set Found to false if string values do not match}
					END;
			UNTIL ((Found) OR (MessPointer > i));
			
			IF (MessPointer > i) THEN
				Found := FALSE;
				
			IF (Found) THEN
				BEGIN
					FOR Count := 1 TO i DO
						IF ((((Count - 1) MOD NumKeysNeeded) + 1) = (((MessPointer - 1) MOD NumKeysNeeded) + 1)) THEN
							MessAnswer[Count] := Mess[Count];
					Key[(((MessPointer - 1) MOD NumKeysNeeded) + 1)] := TestKey;  {store current key in appropriate place in array}
					NumKeysFound := NumKeysFound + 1;  {increment number of keys found}			
				END;  {if structure}
		END;  {for loop - CharCycle}
	
	TestKey := TestKey + 1;  {Increment current "key"/"offset"}

UNTIL ((NumKeysFound = NumKeysNeeded) OR (TestKey > CharRange));
			
{Answer Key Display Module}
WRITELN;
WRITELN('The encryption key is : ');
FOR Count := 1 TO NumKeysNeeded DO
	WRITE(Key[Count]:1, '  ');
WRITELN;

{Decoded Message Display Module}
WRITELN;
WRITELN('The message is : ');
FOR Count := 1 TO i DO
	WRITE(MessAnswer[Count]);
WRITELN;

END.  {of program}