//---------------------------------------------------------------------------
// Contraband Hell Edition - Immortalware JTHZ
// Concept & programming:    Julius B. Thyssen & Hens Zimmerman
// http://come.to/us         mailto:jthz@usa.net
// Programmed using          Borland C++ Builder 3.0 Professional
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
const String regKey="SOFTWARE\\Immortalware JTHZ\\Contraband Hell Edition";
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    Timer1Timer(NULL); // first commercial pleaze
    checkBackup->Checked=(RegGet("Backup")!="Nope");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::checkIDEAClick(TObject *Sender)
{
    editPW1->Enabled=checkIDEA->Checked;
    editPW2->Enabled=checkIDEA->Checked;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnDataClick(TObject *Sender)
{
    const String regStr="Data directory";
    String dir=RegGet(regStr);
    if(dir.Length()&&DirectoryExists(dir))
        OpenDialog1->InitialDir=dir;
    if(OpenDialog1->Execute()){
        labelData->Caption=OpenDialog1->FileName;
        RegPut(regStr,ExtractFilePath(OpenDialog1->FileName));
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnBMPClick(TObject *Sender)
{
    const String regStr="Bitmap directory";
    String dir=RegGet(regStr);
    if(dir.Length()&&DirectoryExists(dir))
        OpenPictureDialog1->InitialDir=dir;
    if(OpenPictureDialog1->Execute()){
        // check if this is the right kinda BMP
        Graphics::TBitmap *pBMP=new Graphics::TBitmap();
        try{
            pBMP->LoadFromFile(OpenPictureDialog1->FileName);
            if((pBMP->HandleType==bmDIB)&&(pBMP->PixelFormat==pf24bit))
                labelBMP->Caption=OpenPictureDialog1->FileName;
            else
                ShowMessage("Not a true color device independant BMP");
        }
        catch(...){
            ShowMessage("Error loading "+OpenPictureDialog1->FileName);
        }
        delete pBMP;
        RegPut(regStr,ExtractFilePath(OpenPictureDialog1->FileName));
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnFinishClick(TObject *Sender)
{
    if(!labelData->Caption.Length()){
        ShowMessage("No datafile selected yet");
        return;
    }
    if(!labelBMP->Caption.Length()){
        ShowMessage("No bitmap selected yet");
        return;
    }
    bool encrypt=checkIDEA->Checked;
    if(encrypt&&((editPW1->Text!=editPW2->Text)||(!editPW1->Text.Length()))){
        ShowMessage("Enter the same passphrase twice");
        editPW1->SetFocus();
        return;
    }
    DWORD crc32PW=encrypt?crc32(editPW1->Text.c_str(),editPW1->Text.Length()):0L;
    if(encrypt&&!crc32PW){ // very unlikely (1:2^32)
        editPW1->Text="";
        editPW2->Text="";
        ShowMessage("Pick a different passphrase");
        editPW1->SetFocus();
        return;
    }
    btnFinish->Enabled=false;
    Screen->Cursor=crHourGlass;
    Animate1->Visible=true;
    Animate1->Active=true;
    Graphics::TBitmap *pBMP=new Graphics::TBitmap();
    BYTE *hiddenData=NULL;
    try{
        // load bitmap
        try{
            pBMP->LoadFromFile(labelBMP->Caption);
        }
        catch(...){
            throw("Error loading "+labelBMP->Caption);
        }
        // load datafile
        HANDLE hFile=CreateFile(
            labelData->Caption.c_str(),
            GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|
            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|
            FILE_FLAG_SEQUENTIAL_SCAN,
            NULL
        );
        if(hFile==INVALID_HANDLE_VALUE)
            throw("Error reading file: "+labelData->Caption);
        DWORD dataSize=GetFileSize(hFile,NULL);
        if(!dataSize){
            CloseHandle(hFile);
            throw(labelData->Caption+" is empty");
        }
        DWORD embedSize=dataSize;
        // round to 16 for IDEA
        if(encrypt&&(embedSize%16L))
            embedSize+=(16L-(embedSize%16L));
        embedSize+=8L; // 4 for crc32, 4 for filesize
        DWORD containerSize=pBMP->Width*pBMP->Height*3L; // rgb
        containerSize-=3L; // first 3 bytes store dataRate
        BYTE bitRate;
        DWORD neededSize;
        for(bitRate=1;bitRate<9;bitRate++){
            neededSize=(embedSize*8L)/bitRate;
            if((embedSize*8L)%bitRate)
                neededSize++;
            if(containerSize>=neededSize)
                break;
        }
        if(bitRate>8){
            CloseHandle(hFile);
            throw(String("Too much data for this bitmap"));
        }
        if(bitRate>1){
            String warning="Bitrate is "+IntToStr((int)bitRate)+" (";
            switch(bitRate){
                case 2:
                    warning+="may be visible";
                    break;
                case 3:
                case 4:
                    warning+="visible";
                    break;
                case 5:
                case 6:
                case 7:
                    warning+="very visible";
                    break;
                case 8:
                    warning+="random snow";
                    break;
            }
            warning+=")\nProceed?";
            Animate1->Visible=false;
            Animate1->Active=false;
            Screen->Cursor=crDefault;
            if(MessageBox(Form1->Handle,warning.c_str(),"Warning!!!",
                    MB_YESNO)==IDNO){
                delete pBMP;
                btnFinish->Enabled=true;
                return;
            }
            Animate1->Visible=true;
            Animate1->Active=true;
            Screen->Cursor=crHourGlass;
        }
        // allow one byte for possible slack if dataRate>1
        hiddenData=new BYTE[embedSize+1];
        ZeroMemory(hiddenData,embedSize+1);
        union{ // anonymous union to translate DWORDs
            BYTE uBYTES[4];
            DWORD uDWORD;
        };
        // crc32 of our IDEA passphrase (or 0 for none)
        uDWORD=crc32PW;
        CopyMemory(hiddenData,uBYTES,sizeof(DWORD));
        // we'll embed the actual size of file (not IDEA rounded);
        // the extractor will know when more bytes were embedded
        uDWORD=dataSize;
        CopyMemory(&hiddenData[4],uBYTES,sizeof(DWORD));
        // actual data (not yet encrypted)
        DWORD dwNumberOfBytesRead;
        ReadFile(hFile,(LPVOID)&hiddenData[8],uDWORD,&dwNumberOfBytesRead,NULL);
        CloseHandle(hFile);
        if(dwNumberOfBytesRead!=uDWORD)
            throw("Error reading data from "+labelData->Caption);
        if(encrypt) // do not encrypt crc or datalength (duh..)
            Encrypt(&hiddenData[8],embedSize-8L,(BYTE*)editPW1->Text.c_str());
        // embed dataRate in the first 3 bytes (at dataRate 1 always)
        BYTE *ptr=(BYTE*)pBMP->ScanLine[0];
        for(int i=0;i<3;i++){
            ptr[i]&=0xfe; // zero out lsb
            ptr[i]|=(BYTE)(((bitRate-1)>>i)&1); // translate to [0..7] scale
        }
        DWORD byteCtr=0L;
        int bitCtr=0;
        RandDev r; // initialize l'Ecuyer PRNG
        for(int y=0;y<pBMP->Height;y++){
            ptr=(BYTE*)pBMP->ScanLine[y];
            for(int x=0;x<(3*pBMP->Width);x++){ // rgb true color
                if(!y&&x<3) // this is where the bitRate is embedded already
                    continue;
                // zero out part of the byte (depends on bitrate)
                ptr[x]&=(BYTE)(0xff<<bitRate);
                // OR it with hidden data
                BYTE nextByte=0;
                for(int i=0;i<bitRate;i++){
                    nextByte<<=1;
                    if(byteCtr<embedSize){
                        nextByte|=(BYTE)((hiddenData[byteCtr]>>bitCtr)&1);
                        if(++bitCtr>7){ // byte processed
                            bitCtr=0;
                            ++byteCtr;
                        }
                    }
                    else // pad with random data for increased security
                        nextByte|=(BYTE)(r.rnd(2L)?1:0);
                }
                ptr[x]|=nextByte;
            }
        }
        // backup file if needed:
        if(checkBackup->Checked){
            String tmpStr=labelBMP->Caption;
            tmpStr.SetLength(tmpStr.Length()-2);
            tmpStr+="ak";
            if(!CopyFile(labelBMP->Caption.c_str(),tmpStr.c_str(),false))
                throw(String("Error writing backup (disk full? read only?)"));
        }
        try{
            pBMP->SaveToFile(labelBMP->Caption);
        }
        catch(...){
            throw(String("Error re-writing bitmap (disk full? read only?)"));
        }
        delete pBMP;
        delete hiddenData; // save for NULL
        Animate1->Visible=false;
        Animate1->Active=false;
        Screen->Cursor=crDefault;
        btnFinish->Enabled=true;
        ShowMessage(labelData->Caption+"\nsuccesfully embedded in\n"+
                labelBMP->Caption);
    }
    catch(String s){
        delete pBMP;
        delete hiddenData; // save for NULL
        Screen->Cursor=crDefault;
        Animate1->Visible=false;
        Animate1->Active=false;
        ShowMessage(s);
        btnFinish->Enabled=true;
        return;
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnXBMPClick(TObject *Sender)
{
    const String regStr="Bitmap X-directory";
    String dir=RegGet(regStr);
    if(dir.Length()&&DirectoryExists(dir))
        OpenPictureDialog1->InitialDir=dir;
    if(OpenPictureDialog1->Execute()){
        // check if this is the right kinda BMP
        Graphics::TBitmap *pBMP=new Graphics::TBitmap();
        try{
            pBMP->LoadFromFile(OpenPictureDialog1->FileName);
            if((pBMP->HandleType==bmDIB)&&(pBMP->PixelFormat==pf24bit))
                labelXBMP->Caption=OpenPictureDialog1->FileName;
            else
                ShowMessage("Not a true color device independant BMP");
        }
        catch(...){
            ShowMessage("Error loading "+OpenPictureDialog1->FileName);
        }
        delete pBMP;
        RegPut(regStr,ExtractFilePath(OpenPictureDialog1->FileName));
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnXDataClick(TObject *Sender)
{
    const String regStr="Save directory";
    String dir=RegGet(regStr);
    if(dir.Length()&&DirectoryExists(dir))
        SaveDialog1->InitialDir=dir;
    SaveDialog1->Filter="Anything goes (*.*)|*.*|Text files (*.txt)|*.txt|Document files (*.doc)|*.doc";
    SaveDialog1->FileName="Nameless.txt";
    if(SaveDialog1->Execute()){
        editXData->Text=SaveDialog1->FileName;
        RegPut(regStr,ExtractFilePath(SaveDialog1->FileName));
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnXFinishClick(TObject *Sender)
{
    if(!labelXBMP->Caption.Length()){
        ShowMessage("No bitmap selected yet");
        return;
    }
    if(!editXData->Text.Length()){
        ShowMessage("No datafile selected yet");
        editXData->SetFocus();
        return;
    }
    btnXFinish->Enabled=false;
    Screen->Cursor=crHourGlass;
    Animate2->Visible=true;
    Animate2->Active=true;
    Graphics::TBitmap *pBMP=new Graphics::TBitmap();
    BYTE *hiddenData=NULL;
    try{
        try{
            pBMP->LoadFromFile(labelXBMP->Caption);
        }
        catch(...){
            throw("Error loading "+labelXBMP->Caption);
        }
        // read bitrate from first 3 bytes
        BYTE bitRate=0;
        BYTE *ptr=(BYTE*)pBMP->ScanLine[0];
        for(int i=0;i<3;i++){
            ptr[i]&=1;
            bitRate|=(BYTE)(ptr[i]<<i);
        }
        ++bitRate; // back to [1..8] scale
        union{ // // anonymous union to translate DWORDs
            BYTE uBYTES[4];
            DWORD uDWORD;
        };
        DWORD byteCtr;
        int bitCtr;
        byteCtr=0L;
        bitCtr=0;
        // easiest solution here; allocate buffer as big as maximal
        // embedding for this bitRate and pull everything out at once...
        DWORD bufSize=(pBMP->Width*pBMP->Height*3L*bitRate)/8L;
        hiddenData=new BYTE[bufSize];
        ZeroMemory(hiddenData,bufSize);
        for(int y=0;y<pBMP->Height;y++){
            ptr=(BYTE*)pBMP->ScanLine[y];
            for(int x=0;x<(3*pBMP->Width);x++){ // rgb true color
                if(!y&&x<3) // this is where the bitRate is embedded
                    continue;
                for(int i=(bitRate-1);i>=0;i--){ // reverse pullout order
                    hiddenData[byteCtr]|=(BYTE)(((ptr[x]>>i)&1)<<bitCtr);
                    if(++bitCtr>7){ // byte processed
                        bitCtr=0;
                        ++byteCtr;
                    }
                }
            }
        }
        CopyMemory(uBYTES,hiddenData,sizeof(DWORD));
        DWORD userPW=crc32(editXPW->Text.c_str(),editXPW->Text.Length());
        DWORD EmbeddedCRC32=uDWORD;
        if(EmbeddedCRC32&&(userPW!=EmbeddedCRC32))
            throw(String("Wrong passphrase"));
        // get actual filesize
        CopyMemory(uBYTES,&hiddenData[4],sizeof(DWORD));
        DWORD dataSize=uDWORD;
        if(!dataSize||dataSize>bufSize)
            throw(String("No Contraband compliant data in this BMP"));
        DWORD embedSize=dataSize;
        // round to 16 for IDEA
        if(EmbeddedCRC32&&(embedSize%16L))
            embedSize+=(16L-(embedSize%16L));
        if(EmbeddedCRC32)
            Decrypt(&hiddenData[8],embedSize,(BYTE*)editXPW->Text.c_str());
        // write to file
        DWORD dwWritten;
        HANDLE hFile=CreateFile(
            editXData->Text.c_str(),
            GENERIC_WRITE,
            0,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            NULL
        );
        if(hFile!=INVALID_HANDLE_VALUE){
            WriteFile(hFile,(LPVOID)&hiddenData[8],dataSize,&dwWritten,NULL);
            CloseHandle(hFile);
        }
        else
            throw("Error creating "+editXData->Text);
        if(dwWritten!=dataSize)
            throw(String("Error writing data (disk full?)"));
        btnXFinish->Enabled=true;
        Screen->Cursor=crDefault;
        Animate2->Visible=false;
        Animate2->Active=false;
        delete pBMP;
        delete hiddenData;
        ShowMessage(editXData->Text+"\nsuccesfully extracted from\n"+
                labelXBMP->Caption);
    }
    catch(String s){
        btnXFinish->Enabled=true;
        Screen->Cursor=crDefault;
        Animate2->Visible=false;
        Animate2->Active=false;
        delete pBMP;
        delete hiddenData; // save for NULL
        ShowMessage(s);
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::PageControl1Change(TObject *Sender)
{
    PlaySound("W1",HInstance,SND_RESOURCE|SND_ASYNC);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnSourcecodeClick(TObject *Sender)
{
    // save our own sourcecode to a file
    DWORD dwSize,dwWritten=0;
    const String regStr="Source directory";
    String dir=RegGet(regStr);
    if(dir.Length()&&DirectoryExists(dir))
        SaveDialog1->InitialDir=dir;
    SaveDialog1->Filter="ZIP|*.zip";
    SaveDialog1->FileName="contrab.zip";
    if(SaveDialog1->Execute()){
        HRSRC hResInfo=FindResource(HInstance,"ZIPFILE",RT_RCDATA);
        HGLOBAL hGlobal=LoadResource(HInstance,hResInfo);
        dwSize=SizeofResource(HInstance,hResInfo)+1;
        LPVOID pData=LockResource(hGlobal);
        HANDLE hFile=CreateFile(
            SaveDialog1->FileName.c_str(),
            GENERIC_WRITE,
            0,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            NULL
        );
        if(hFile!=INVALID_HANDLE_VALUE){
            WriteFile(hFile,pData,dwSize,&dwWritten,NULL);
            CloseHandle(hFile);
        }
    	FreeResource(hGlobal);
        if(dwWritten!=dwSize) // something went awry here
            ShowMessage("File could not be saved (disk full? read only?)");
        RegPut(regStr,ExtractFilePath(SaveDialog1->FileName));
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
    static RandDev r;
    const int MAX_FLIES=23;
    String blurbFlies[MAX_FLIES]={
        "i.am/_-",
        "i.am/j.",
        "i.am/2u",
        "hey.to/_-",
        "hey.to/j.",
        "i.am/fake",
        "come.to/us",
        "i.am/u.are",
        "come.to/soon",
        "hey.to/u.are",
        "come.to/Julius",
        "come.to/my.ass",
        "vlug.nu/meteen",
        "see.mypage.org",
        "kill.mypage.org",
        "jolanda.tsx.org",
        "loser.mypage.org",
        "come.to/the.lord",
        "come.to/my.island",
        "welcome.to/something",
        "stinking.home.ml.org",
        "www.GalaxyCorp.com/009",
        "www.RadioLink.net/klootzak"
    };
    labelSFly->Caption="http://"+blurbFlies[r.rnd(MAX_FLIES)];
    labelFly->Caption=labelSFly->Caption;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Memo1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
    String cmd;
    if(Y>17&&Y<35)
        cmd="mailto:jthz@usa.net";
    else if(Y>35&&Y<52)
        cmd="http://come.to/us";
    else if(Y>86&&Y<104)
        cmd="mailto:j@i.am";
    else if(Y>104&&Y<121)
        cmd="mailto:hens@gmx.de";
    if(cmd.Length())
        ShellExecute(0,NULL,cmd.c_str(),NULL,NULL,SW_NORMAL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::labelFlyMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
    ShellExecute(0,NULL,labelFly->Caption.c_str(),NULL,NULL,SW_NORMAL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Embed1Click(TObject *Sender)
{
    PageControl1->ActivePage=TabSheet1;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Extract1Click(TObject *Sender)
{
    PageControl1->ActivePage=TabSheet2;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::About1Click(TObject *Sender)
{
    PageControl1->ActivePage=TabSheet3;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Quit1Click(TObject *Sender)
{
    Close();
}
//---------------------------------------------------------------------------
String TForm1::RegGet(String name){
    String s="";
    TRegistry*reg=new TRegistry();
    reg->RootKey=HKEY_CURRENT_USER;
    reg->OpenKey(regKey,true);
    try{
        if(reg->ValueExists(name))
            s=reg->ReadString(name);
    }
    catch(...){
    }
    reg->CloseKey();
    delete reg;
    return s;
}
//---------------------------------------------------------------------------
void TForm1::RegPut(String name,String value){
    // burns var with value in Registry
    TRegistry*reg=new TRegistry();
    reg->RootKey=HKEY_CURRENT_USER;
    reg->OpenKey(regKey,true);
    try{
        reg->WriteString(name,value);
    }
    catch(...){
    }
    reg->CloseKey();
    delete reg;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::checkBackupClick(TObject *Sender)
{
    RegPut("Backup",checkBackup->Checked?"Yeah":"Nope");
}
//---------------------------------------------------------------------------


