/*
 * Author:       Simon Dexter
 * Source:       Unit1.cpp
 * Project:      File Cipher
 * Name:         File Cipher
 * Description:  Main source file
 * Date started: 01/13/09
 * Date modified:
 *               01/14/09 10:35 pm
 *               01/15/09 10:32 pm
 *               01/16/09 07:45 am
 *               01/19/09 12:56 pm
 *                      ParamStr(1); extracts the command line parameter
 *                      i.e. file name passed on by windows explorer
 *
*/

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

const int blockSize = 65536;
TForm1 *Form1;
// these variables are RC4 facilities
unsigned char S[256];
unsigned int i, j;



__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {

}


// loading target file
void __fastcall TForm1::Button1Click(TObject *Sender) {
        this->OpenDialog1->Execute();
        this->loadedFile = OpenDialog1->FileName;
        this->Edit1->Caption = this->loadedFile;
}
// loading key file
void __fastcall TForm1::Button4Click(TObject *Sender)
{
        this->OpenDialog1->Execute();
        this->keyFile = OpenDialog1->FileName;
        this->Edit2->Caption = this->keyFile;
}

// ---------------------------------------------------------------------------
/* status:           completed
 * description:
 *  XOR cipher implementaiton, key file must be larger or equal in size to
 *  the message file
 * date started:     01/14/09
 * date modified:    01/15/09
 * date completed:   01/15/09
 */
void __fastcall TForm1::Button3Click(TObject *Sender) {
    int bufferSize = blockSize;     // arbitrary choice of buffer size
    int difference = 0;                 // difference between current position and file size
    int targetFilePointer = 0;       // starting at the beginning of the target file
    int targetFileSize = 0;
    int keyFileSize = 0;
    fstream fileStream;        // using fstream for binary i/o for target file
    fstream keyStream;         // using fstream for binary i/o for key file
    // target file is opened on this line
    // all flags must be present for successful completition of this step
    fileStream.open(this->loadedFile.c_str(), ios::in | ios::out | ios::binary);

    // verifying if target file was opened successfully
    if (!fileStream.is_open()) {
            ShowMessage("Unable to load specified target file");
            return;
    }
    // key file is opened on this line
    keyStream.open(this->keyFile.c_str(), ios::in | ios::binary);

    if (!keyStream.is_open()) {
            ShowMessage("Unable to load specified key file");
            fileStream.close(); // closing already opened target file stream
            return;
    }


    // setting the reading position of the target file stream to the end
    fileStream.seekg (0, ios::end);
    // getting the position of the reading pointer, thereby retrieving target file size
    targetFileSize = fileStream.tellg();
    // setting position of the reading pointer to the start of the file
    fileStream.seekg (0, ios::beg);

    keyStream.seekg (0, ios::end);
    keyFileSize = keyStream.tellg();
    keyStream.seekg (0, ios::beg);

    if (keyFileSize < targetFileSize) {
        ShowMessage ("Unable to carry out XOR cipher. Key file is smaller than target file.");
        fileStream.close();
        keyStream.close();
        return;
    }
    // adjusting interface
    this->ProgressBar1->Min = 0;
    this->ProgressBar1->Max = targetFileSize;
    this->ProgressBar1->Position = 0;
    this->Label2->Caption =  targetFileSize;
    // loop until file pointer has reached the end of the target file
    while (targetFilePointer < targetFileSize) {
        // initializing byte array of certain specified size
        char buffer [blockSize];
        char keyBuffer [blockSize];
        // calculating difference between the current position in the file and file size
        difference = targetFileSize - targetFilePointer;
        // if the difference is less than the buffer size, then no need to fill the
        // buffer completely, only by the difference
        if (difference < bufferSize) {
                bufferSize = difference;
        }
        fileStream.seekg (targetFilePointer);
        fileStream.read (buffer, bufferSize);

        keyStream.seekg (targetFilePointer);
        keyStream.read (keyBuffer, bufferSize);
        for (int i = 0; i < bufferSize; i++){
                // inverting every byte in the buffer
                buffer[i] = buffer[i] ^ keyBuffer [i];
        }
        // setting writing pointer to the current location
        fileStream.seekp (targetFilePointer);
        // writing the inverted file
        fileStream.write (buffer, bufferSize);
        // incrementing current file pointer by the amount of buffer
        targetFilePointer += bufferSize;
        // adjusting interface
        this->Label1->Caption =  targetFilePointer;
        this->ProgressBar1->Position = targetFilePointer;
    }
    // closing the target file stream
    fileStream.close();
    // closing the key file stream
    keyStream.close();
    // resetting interface
    ShowMessage("Cipher completed");
    this->ProgressBar1->Position = 0;
    this->Label1->Caption = 0;
    this->Label2->Caption = 0;
}


//---------------------------------------------------------------------------
/* status:           completed
 * description:
 *              general subroutine responsible for iterating over every byte in
 *              in the target file and modifying based on mode
 * date started:     01/13/09
 * date modified:    02/14/09 7:58 pm
 * date completed:   02/14/09 7:58 pm
 */

void TForm1::byteCipher(int mode){
    int bufferSize = blockSize;     // arbitrary choice of buffer size
    int difference = 0;         // difference between current position and file size
    int filePointer = 0;        // starting at the beginning of a file
    fstream fileStream;         // using fstream for binary i/o
    // file is opened on this line
    // all flags must be present for successful completition of this step
    fileStream.open(this->loadedFile.c_str(), ios::in | ios::out | ios::binary);

    // verifying if file was opened successfully
    if (!fileStream.is_open()) {
            ShowMessage("Unable to load specified file");
            return;
    }

    // setting the reading position of the file stream to the end
    fileStream.seekg (0, ios::end);
    // getting this position of the reading pointer, thereby retrieving file size
    int fileSize = fileStream.tellg();
    // setting position of the reading pointer to the start of the file
    fileStream.seekg (0, ios::beg);
    // adjusting interface
    this->ProgressBar1->Min = 0;
    this->ProgressBar1->Max = fileSize;
    this->ProgressBar1->Position = 0;
    this->Label2->Caption =  fileSize;
    // RC4 setup
    unsigned char * key = this->Edit3->Text.c_str(); //setting up RC4 key
    this->rc4_init(key, this->Edit3->Text.Length() ); // setting up RC4 using the password
    // loop until file pointer has reached the end of the file
    while (filePointer < fileSize) {
        // initializing byte array of certain specified size
        char buffer[blockSize];
        // calculating difference between the current position in the file and file size
        difference = fileSize - filePointer;
        // if the difference is less than the buffer size, then no need to fill the
        // buffer completely, only by the difference
        if (difference < bufferSize) {
                bufferSize = difference;
        }
        fileStream.seekg(filePointer);
        fileStream.read(buffer, bufferSize);
        for (int i = 0; i < bufferSize; i++){
                // going over every byte in the file
                switch (mode) {
                        case 0: // inversion
                                buffer[i] = ~buffer[i];
                                break;
                        case 1: // cycle
                                buffer [i] = cycle (buffer [i]);
                                break;
                        case 2: // RC4
                                buffer [i] = buffer [i] ^ rc4_output();
                                break;
                }

        }
        // setting writing pointer to the current location
        fileStream.seekp(filePointer);
        // writing the inverted file
        fileStream.write(buffer, bufferSize);
        // incrementing current file pointer by the amount of buffer
        filePointer += bufferSize;
        // adjusting interface
        this->Label1->Caption =  filePointer;
        this->ProgressBar1->Position = filePointer;
        // added on 02/14/09 7:58 pm
        Application->ProcessMessages();
        Sleep(1);
    }
    // closing the stream
    fileStream.close();
    // resetting interface
    ShowMessage("Cipher completed");
    this->ProgressBar1->Position = 0;
    this->Label1->Caption = 0;
    this->Label2->Caption = 0;

}

//---------------------------------------------------------------------------
/* status:           completed
 * description:
 *  byte cycling algorithm
 * date started:     01/16/09
 * date modified:    01/16/09
 * date completed:   01/16/09 7:44 am
 */
char TForm1::cycle (char value) {
        int leftMask = 170;
        int rightMask = 85;

        int iLeft = value & leftMask;
        int iRight = value & rightMask;

        iLeft = iLeft >> 1;
        iRight = iRight << 1;

        return iLeft | iRight;
}
//---------------------------------------------------------------------------
// invokation of byte inversion cipher
void __fastcall TForm1::Button2Click(TObject *Sender)
{
        this->byteCipher (0);
}
//---------------------------------------------------------------------------
// invokation of cycle cipher
void __fastcall TForm1::Button5Click(TObject *Sender){
        this->byteCipher (1);
}
//---------------------------------------------------------------------------




void __fastcall TForm1::FormCreate(TObject *Sender) {
// ParamStr(0) is the path + name of the executable file of this program
        if (ParamStr(1).Length()  != 0) {
                this->loadedFile = ParamStr(1);
                this->Edit1->Caption = this->loadedFile;
        }
}
//---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/* status:           prototype
 * description:
 *  XOring with RC4 stream initiated using password as key
 * date started:     01/26/09
 * date modified:    02/02/09
 * date completed:   02/02/09
 */
void __fastcall TForm1::Button6Click(TObject *Sender) {
        if (Edit3->Text.Length() < 8) {
            ShowMessage("Password has to be at least 8 characters long");
            return;

        }
        if (Edit3->Text.Length() > 256) {
            ShowMessage("Password can be 256 characters maximum");
            return;
        }
        this->byteCipher (2);
}


//---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/* status:           completed
 * description:
 *      RC4 stream initializer
 * date started:     02/02/09
 * date modified:    02/02/09
 * date completed:   02/02/09
 */

void TForm1::rc4_init(unsigned char *key, unsigned int key_length) {
    for (i = 0; i < 256; i++)
        S[i] = i;
 
    for (i = j = 0; i < 256; i++) {
        unsigned char temp;
 
        j = (j + key[i % key_length] + S[i]) & 255;
        temp = S[i];
        S[i] = S[j];
        S[j] = temp;
    }
 
    i = j = 0;
}

//---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/* status:           complted
 * description:
 *      RC4 stream byte generator
 * date started:     02/02/09
 * date modified:    02/02/09
 * date completed:   02/02/09
 */

unsigned char TForm1::rc4_output() {
    unsigned char temp;
    i = (i + 1) & 255;
    j = (j + S[i]) & 255;

    temp = S[i];
    S[i] = S[j];
    S[j] = temp;

    return S[(S[i] + S[j]) & 255];
}
//---------------------------------------------------------------------------

