unit uMainFrm;
interface
(* ==================================================================== *)
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, Menus, uAboutBox, uStegHide;
type
  TMainFrm = class(TForm)
    MainMenu: TMainMenu;
    Datei1: TMenuItem;
    OpenMenuE: TMenuItem;
    SaveMenuE: TMenuItem;
    N1: TMenuItem;
    EndMenuE: TMenuItem;
    AboutMenuE: TMenuItem;
    Funktionen1: TMenuItem;
    DechiffrierenMenuE: TMenuItem;
    ChiffrierentMenuE: TMenuItem;
    Image: TImage;
    DataBitLbl: TLabel;
    DataBitCBox: TComboBox;
    MaxTextLbl1: TLabel;
    BmpOpenDialog: TOpenDialog;
    TextLengthLbl1: TLabel;
    TextLengthLbl2: TLabel;
    MaxTextLbl2: TLabel;
    N2: TMenuItem;
    BildanzeigenMenuE: TMenuItem;
    BmpSaveDialog: TSaveDialog;
    GeheimTextMemo: TMemo;
    TextladenMenuE: TMenuItem;
    TextspeichernMenuE: TMenuItem;
    TxtOpenDialog: TOpenDialog;
    TxtSaveDialog: TSaveDialog;
    ClearTextBtn: TMenuItem;
    procedure LadeBmp(Sender : TObject);       // lädt des bmp bei Auswahl von Datei/Laden
    procedure LadeTextDatei(Sender : TObject);
    procedure EndMenuEClick(Sender: TObject);
    procedure AboutMenuEClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ChiffrierentMenuEClick(Sender: TObject);
    procedure DataBitCBoxChange(Sender: TObject);
    procedure DechiffrierenMenuEClick(Sender: TObject);
    procedure TextChange(Sender: TObject);
    procedure BildanzeigenMenuEClick(Sender: TObject);
    procedure SpeicherBmp(Sender: TObject);
    procedure SpeicherTxt(Sender: TObject);
    procedure ClearTextBtnClick(Sender: TObject);
  private
    { Private-Deklarationen }
    steghide : TStegHide;
    bmp_geladen, bmp_geaendert, txt_geladen : boolean;
    bmp_DateiName : string;
    txt_DateiName : string;
    function EndsWith (text,teststring : string):boolean;
    procedure MaskeAktualisieren;
    function TextLaenge : integer;
  public
    { Public-Deklarationen }
  end;
var
  MainFrm: TMainFrm;
implementation
(* ==================================================================== *)
{$R *.DFM}
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
// Private
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
function TMainFrm.EndsWith;
(* -------------------------------------------------------------------- *)
var
   textlength, testlength : integer;
   vergleichstring : string;
begin
     Result := false;
     testlength := length(teststring);
     textlength := length (text);
     if textlength > testlength then
     begin
          vergleichstring := copy (text,(textlength+1)-testlength,testlength);
          if vergleichstring = teststring then
          result := true
     end;
end;
procedure TMainFrm.MaskeAktualisieren;
(* -------------------------------------------------------------------- *)
begin
     if BildanzeigenMenuE.Checked then Image.Picture.Bitmap := steghide.getBmp;
     Invalidate;
end;
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
// Form-routines
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
procedure TMainFrm.LadeBmp;
begin
     if BmpOpenDialog.Execute then
     begin
	  bmp_DateiName:= BmpOpenDialog.FileName;
          if not steghide.LoadBmp(bmp_DateiName) then
          begin
               ShowMessage('Bitmap ' + bmp_Dateiname + ' konnte nicht geladen werden!');
               exit;
          end;
          MainFrm.Caption := 'PGH - ' + bmp_DateiName;
          MaxTextLbl2.Caption := IntToStr(steghide.GetMaxTextLength);
          bmp_geladen := true;
          MaskeAktualisieren;
     end;
end;
procedure TMainFrm.LadeTextDatei;
(* -------------------------------------------------------------------- *)
var datei : Textfile;
    tmpstring : string;
begin
     if txt_DateiName <> '' then TxtOpenDialog.FileName := txt_DateiName;
     if TxtOpenDialog.Execute then
     begin
          txt_DateiName := TxtOpenDialog.FileName;
	  assignfile(datei, txt_DateiName);
	  {$i-} Reset(datei); {$i+}
          if IOResult <> 0 then exit;
	  if GeheimTextMemo.Lines.Count > 0 then
		if MessageDlg('Soll der bestehende Text gelöscht werden?',
				mtConfirmation, [mbYes, mbNo], 0)= mrYes
		then GeheimTextMemo.Clear;
	  while not EOF(datei) do
	  begin 
		readln(datei, tmpstring);
		GeheimTextMemo.Lines.Append(tmpstring);
	  end;
          closefile(datei);
          txt_geladen := true;
     end;
end;
procedure TMainFrm.EndMenuEClick(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     Close;
end;
procedure TMainFrm.AboutMenuEClick(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     AboutBox.Show;
end;
procedure TMainFrm.FormCreate(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     StegHide := TStegHide.Create;
     StegHide.SetText(GeheimTextMemo.Lines);
     //LoadConfig;
     //DatenAktualisieren;
     //StegHide.Init;
     bmp_DateiName := '';
     txt_DateiName := '';
     bmp_geladen := false;
     bmp_geaendert := false;
     txt_geladen := false;
     DataBitCBox.ItemIndex := 0;
     TextLengthLbl2.Caption := IntToStr(Textlaenge);
end;
procedure TMainFrm.FormClose(Sender: TObject; var Action: TCloseAction);
(* -------------------------------------------------------------------- *)
begin
     if bmp_geaendert then
        case MessageDlg('Soll das geänderte Bild gespeichert werden?', mtConfirmation,
             [mbYes, mbNo, mbCancel], 0) of
                     id_Yes: SpeicherBmp(Self);
                     id_Cancel: begin Action := caNone; exit; end;
        end;
     StegHide.Free;
end;
procedure TMainFrm.ChiffrierentMenuEClick(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     if (GeheimTextMemo.Lines.Count > 0) and bmp_geladen then
     begin
          steghide.SetText(GeheimTextMemo.Lines);
          if not steghide.Chiffrieren then ShowMessage('Text wurde nicht korrekt chiffriert!');
          bmp_geaendert := true;
          MaskeAktualisieren;
     end
     else ShowMessage('Bitte Bitmap laden und einen Text eingeben!');
end;
procedure TMainFrm.DataBitCBoxChange(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     steghide.SetDatenBit(DataBitCBox.ItemIndex);
end;
procedure TMainFrm.DechiffrierenMenuEClick(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     if bmp_geladen then
     begin
          GeheimTextMemo.Clear;
          steghide.Dechiffrieren;
     end
     else ShowMessage('Bitte Bitmap zum dechiffrieren laden!');
end;
procedure TMainFrm.TextChange(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     TextLengthLbl2.Caption := IntToStr(Textlaenge);
end;
procedure TMainFrm.BildanzeigenMenuEClick(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     if BildanzeigenMenuE.Checked then
     begin
          BildanzeigenMenuE.Checked := false;
          Image.Hide;
     end
     else
     begin
          BildanzeigenMenuE.Checked := true;
          Image.Visible := true;
          MaskeAktualisieren;
     end;
end;
procedure TMainFrm.SpeicherBmp(Sender: TObject);
(* -------------------------------------------------------------------- *)
var tempbmp : TBitmap;
begin
     if not bmp_geladen then begin ShowMessage('Bitte ein Bitmap laden!'); exit; end;
     if BmpSaveDialog.Execute then
     begin
          if fileexists(BmpSaveDialog.FileName) then
             if MessageDlg('Soll die Datei '+ BmpSaveDialog.FileName + ' wirklich überschrieben werden?', mtConfirmation,
                                 [mbYes, mbNo], 0)= mrNo
             then SpeicherBmp(Self);
             
          if not endswith(BmpSaveDialog.FileName, '.bmp')
          then BmpSaveDialog.FileName:= BmpSaveDialog.FileName + '.bmp';
          steghide.WriteBmp(BmpSaveDialog.FileName);
          bmp_geaendert := false;
     end;
end;
procedure TMainFrm.SpeicherTxt(Sender: TObject);
(* -------------------------------------------------------------------- *)
var datei : textfile;
    i : integer;
begin
     if GeheimTextMemo.Lines.Count = 0 then begin ShowMessage('Bitte einen Text eingeben!'); exit; end;
     if TxtSaveDialog.Execute then
     begin
          if fileexists(TxtSaveDialog.FileName) then
             if MessageDlg('Soll die Datei '+ TxtSaveDialog.FileName + ' wirklich überschrieben werden?', mtConfirmation,
                                 [mbYes, mbNo], 0)= mrNo
             then SpeicherTxt(Self);
          if not endswith(TxtSaveDialog.FileName, '.txt')
          then TxtSaveDialog.FileName:= TxtSaveDialog.FileName + '.txt';
          AssignFile(datei, TxtSaveDialog.FileName);
          {$i-} Rewrite(datei); {$i+}
          if IOResult <> 0 then begin ShowMessage('Text nicht gespeichert!'); exit; end;
          for i:= 0 to GeheimTextMemo.Lines.Count-1 do
              writeln(datei, GeheimTextMemo.Lines[i]);
          closefile(datei);
     end;
end;
function TMainFrm.TextLaenge;
(* -------------------------------------------------------------------- *)
var i : integer;
begin
     Result := 0;
     for i := 0 to GeheimTextMemo.Lines.Count-1 do
         inc(Result,length(GeheimTextMemo.Lines[i]));
end;
procedure TMainFrm.ClearTextBtnClick(Sender: TObject);
(* -------------------------------------------------------------------- *)
begin
     if MessageDlg('Soll der Text wirklich gelöscht werden?', mtConfirmation,
        [mbYes, mbNo], 0)= mrYes
     then GeheimTextMemo.Clear;
end;
end.

unit uStegHide;
/////////////////////////////////////////////////////////////////////////
// Klasse zum Erzeugen eines steganographischen Bildes
// Programmierer: Norman Niemer
// Datum: 30.05.00
/////////////////////////////////////////////////////////////////////////
interface
uses graphics {TBitmap}, sysutils {PByteArray}, classes {TStrings};
const BitArr: array[0..7] of byte = (1, 2, 4 , 8 , 16, 32, 64, 128);
      EndOfText = 30;
      EndOfLine = 29;
type
//////////////////////
// TStegHide
//////////////////////
  TStegHide = class
          constructor Create;
          destructor Destroy;
          private
                 bmp:TBitmap;
		 Geheimtext : TStrings;
                 bmp_geladen : boolean;                                      // gibt an, ob ein bmp geladen ist
                 datenbit : byte;                                            // gibt das Bit an, wo die Daten versteckt werden
                 function BitStatus(SetWord, BitNum : Word) : Boolean;       // true, wenn Bit gesetzt
                 procedure ToggleBit( BitNum : byte; var Setbyte : byte);    // tauscht die einzelnen Bits
          public
                function Chiffrieren : boolean;
                function Dechiffrieren : boolean;
                function GetBmp : TBitmap;                                 
                function GetMaxTextLength : integer;                            // gibt Maximalanzahl der vesteckbaren Zeichen zurück
		function GetText : TStrings;
		function LoadBmp( dn : string) : boolean;
                procedure SetDatenBit( dbit : byte );
                procedure SetBitmap( tmpbmp : TBitmap );
		procedure SetText( txt : TStrings );
		function WriteBmp( dn : string) : boolean;
  end; // TStegHide
/////////////////////////////////////////////////////////////////////////
// function chiffrieren
// Aufgabe: versteckt die information im bmp
// vorher: bmp ist geladen
// nachher: information ist versteckt
//
// function dechiffrieren
// Aufgabe: findet versteckte information
// vorher: bmp ist geladen
// nachher: information
//
// function Getmaxtextlenght
// Aufgabe: gibt die maximale Textlänge, die in dem
//          geladenen Bmp versteckt werden kann zurück
// vorher: bmp ist geladen
// Nachher:
//
// funcion loadbmp
// Aufgabe: lädt das bmp
// vorher: bmp_geladen := false
// nachher: bmp_geladen := true
//
// procedure SetDatenBit
// Aufgabe: setzt das Bit, auf welchen die Informationen vesteckt werden
//
// function writeBmp
// Aufgabe: speichert das veränderte bmp
// vorher: bmp ist nicht gespeichert
// nachher: bmp ist gespeichert
/////////////////////////////////////////////////////////////////////////
implementation
//////////////////////////////////////////////////
// General
//////////////////////////////////////////////////
constructor TStegHide.Create;
begin
     inherited Create;
     bmp := TBitmap.Create;
     Geheimtext := TStrings.Create;
     bmp_geladen := false;
     datenbit := 0;
end;
destructor TStegHide.Destroy;
begin
     bmp.Free;
end;
///////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////
function TStegHide.BitStatus(SetWord, BitNum : Word) : Boolean;
begin
   if (SetWord and BitNum) = BitNum then
      Result := true else Result := false;
end;
procedure TStegHide.ToggleBit;
begin
     Setbyte := Setbyte Xor BitNum;
end;
///////////////////////////////////////////////////
// Public
///////////////////////////////////////////////////
function TStegHide.Chiffrieren;
var x, y, for_char, for_string, for_text : integer;
    ascii : byte;
    pixels : PByteArray;
    last_line : boolean;
procedure GetNewLine;
begin
     pixels := bmp.ScanLine[x];
     inc(x);
     if x >= bmp.height then last_line := true;
     y := 0;
end;
begin
     Result := false;
     if not bmp_geladen then exit;
     x := 0; last_line := false;
     GetNewLine;
     if Geheimtext.Count = 0 then exit;
     for for_text := 0 to geheimtext.Count-1 do
     begin
          for for_string := 1 to length(Geheimtext[for_text]) do
          begin
               ascii := ord(geheimtext[for_text][for_string]);
               for for_char := 0 to 7 do
               begin
                 if not ((BitStatus(ascii, BitArr[for_char])) =
                         (BitStatus(pixels[y], BitArr[datenbit])))
                 then ToggleBit(BitArr[datenbit], pixels[y]);
                 inc(y);
                 if y >= bmp.width then
                    if last_line then exit else GetNewLine;
               end;
          end;
          
          ascii := EndOfLine;                 // signalisiert das Ende einer Zeile
          for for_char := 0 to 7 do
          begin
               if not ((BitStatus(ascii, BitArr[for_char])) =
                        (BitStatus(pixels[y], BitArr[datenbit])))
               then ToggleBit(BitArr[datenbit], pixels[y]);
               inc(y);
               if y >= bmp.width then
                    if last_line then exit else GetNewLine;
           end;
     end;
     ascii := EndOfText;                      // signalisiert das Ende des Textes
     for for_char := 0 to 7 do
     begin
                 if not ((BitStatus(ascii, BitArr[for_char])) =
                        (BitStatus(pixels[y], BitArr[datenbit])))
                 then ToggleBit(BitArr[datenbit], pixels[y]);
                 inc(y);
                 if y >= bmp.width then
                    if last_line then exit else GetNewLine;
     end;
     Result := true;
end;
function TStegHide.Dechiffrieren;
var geheimzeile : string;
    ascii : byte;
    for_char, x, y : integer;
    pixels : PByteArray;
    i : integer;                                                         //debug
begin
     for_char := 0; ascii := 0; Geheimtext.Clear; Result := false;
     i := 0;
     for x:= 0 to bmp.Height-1 do
     begin
          pixels := bmp.ScanLine[x];
          for y:= 0 to bmp.Width-1 do
          begin
               if not ((BitStatus(ascii, BitArr[for_char])) =
                        (BitStatus(pixels[y], BitArr[datenbit])))
               then ToggleBit(BitArr[for_char], ascii);
               inc(for_char);
               if for_char = 8 then
               begin
                    if ascii = EndOfText then begin Result := true; exit; end;    // Ende des Textes wurde erreicht
                    if ascii = EndOfLine                                          // Ende einer Zeile
		    then begin Geheimtext.Append(geheimzeile); geheimzeile := ''; end
		    else geheimzeile := geheimzeile + Chr(ascii);
                    for_char := 0;
                    ascii := 0;
               end;
          end;
     end;
end;
function TStegHide.GetBmp;
begin
     Result := bmp;
end;
function TStegHide.GetMaxTextLength;
begin
     Result := ( bmp.width * bmp.height ) div 8;
end;
function TStegHide.GetText;
begin
     Result := TStrings.Create;
     Result := Geheimtext;
end;
function TStegHide.LoadBmp;
begin
     Result := false;
     try
	bmp.LoadFromFile(dn);
     except exit;
     end;
     Result := true;
     bmp_geladen := true;
end;
procedure TStegHide.SetBitmap;
begin
     bmp := tmpbmp;
     if not bmp_geladen then bmp_geladen := true;
end;
procedure TStegHide.SetDatenBit;
begin
     datenbit := dbit;
end;
procedure TStegHide.SetText;
begin
     Geheimtext := txt;
end;
function TStegHide.WriteBmp;
begin
     Result := false;
     try
        bmp.SaveToFile(dn);
     except exit;
     end;
     Result := true;
end;
end.