// Testprogramm ppm_2
// verarbeitet eine Ausgabedatei (Nach Vorlage von Jon Erickson, angepasst)
// g++ -o ppm_2 -l crypt ppm_2.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <crypt.h>

#define HEIGHT 16384
#define WIDTH 1129
#define DEPTH 8
#define SIZE HEIGHT * WIDTH * DEPTH
#define DCM HEIGHT * WIDTH
const char * salt = "je";

// Funktion zur Kodierung eines Klartextzeichens. Rueckgabe hat 6 Bit
int singleval(char a)
{
  int i, j;
  i = (int) a;
  if ((i >= 46) && (i <= 57)) j = i - 46;  // 46=.,57=9
  else if ((i >= 65) && (i <= 90)) j = i - 53;  // 65=A,90=Z
  else if ((i >= 97) && (i <= 122)) j = i - 59;  // 97=a,122=z
  return j;  /* zwischen 0 und 63 */
}

// bekommt drei Werte, wobei vom Dritten nur 2 Bits beachtet werden
// singleval liefert hierbei eine 6-Bit-Zahl zurueck; tripleval liefert 14Bit
int tripleval(char a, char b, char c)
{
  return (((singleval(c) & 3) << 12) + (singleval(a) << 6) + singleval(b));
}


// Verknueft zwei Vektoren per Bitweises UND
void merge (char *vector1, char * vector2)
{
  for (int i = 0; i < WIDTH; i++) vector1[i] &= vector2[i];
}

// Schaut in den Vector(fuer 2 Zeichen) und zaehlt die 95^2 Moeglichkeiten
int length (char *vector)
{
  int count=0;
  for (int i=0; i < 9025; ++i)
  {
    count += ((vector[(i >> 3)] & (1 << (i&7))) >> (i&7));
  }
  return count;
}

// Gibt einen Index im Vector zurueck
int grab (char *vector, int index)
{
  char val; int a, b; int word = 0;
  val = ((vector[(index >> 3)] & (1 << (index&7))) >> (index&7));
  if (!val) index = 31337;
  return index;
}

// Zeigt die 95*92 Moeglichkeiten des Vectors an
void show (char * vector)
{
  int i, a, b, val;
  for (i = 0; i < 9025; ++i)
  {
    val = grab(vector,i);
    if (val != 31337)
    {
      a = val / 95; b = val % 95;
      printf("%c%c ", a+32, b+32);
    }
  }
  printf ("\n");
}

// Teilschritt: fd ist Datei, nr die Nummer des chZeichens, vec ist Zeichenf.
void zwischenschr (FILE *fd, int nr, char *vec1, char *vec2, char *pass)
{
  if (nr < 0 || nr >= 8)
  {
    printf ("Zwischenschritt: ungueltige Parameterwerte"); exit(1);
  }
  short anf = nr & 3; short inp = (anf << 1) + 2;
  fseek(fd, DCM*nr + tripleval(pass[inp], pass[inp+1], pass[inp+2])*WIDTH, SEEK_SET);
  fread((anf == 0) ? vec1 : vec2, WIDTH, 1, fd);
  if (anf > 0) merge(vec1, vec2);
  int len = length(vec1);
  printf ("%d Laenge = %d\t%f\n", anf, len, len*100.0/9025);
}

int main()
{
  char plain[5]; char pass[14];
  // erste zwei Zeichen(vec1), letzte zwei Zeichen(vec2), Zwischenfeld(temp)
  char bin_vector1[WIDTH]; char bin_vector2[WIDTH]; char temp_vector[WIDTH];
  int a,b,i,j,pv1_len=0, pv2_len=0;
  FILE *fd;

  if (!(fd = fopen("4char.ppm", "r")))
  {
    printf("Fehler: ppm-Datei nicht geoeffnet"); exit(1);
  }
  printf ("Eingabe Pwort (Beginn %s): ", salt); scanf("%s", &pass);
  // Salt pruefen
  if ((pass[0] != salt[0]) || (pass[1] != salt[1]))
  { printf ("Zum salt mit Datei nichts ermittelbar\n"); return 1; }

  printf ("Erste 2 Zeichen: \tSaettigung\n");
  zwischenschr (fd, 0, bin_vector1, temp_vector, pass);
  zwischenschr (fd, 1, bin_vector1, temp_vector, pass);
  zwischenschr (fd, 2, bin_vector1, temp_vector, pass);
  zwischenschr (fd, 3, bin_vector1, temp_vector, pass);
  show(bin_vector1);
 
  printf ("Letzte 2 Zeichen: \tSaettigung\n");
  zwischenschr (fd, 4, bin_vector2, temp_vector, pass);
  zwischenschr (fd, 5, bin_vector2, temp_vector, pass);
  zwischenschr (fd, 6, bin_vector2, temp_vector, pass);
  zwischenschr (fd, 7, bin_vector2, temp_vector, pass); 
  show(bin_vector2); fclose(fd) /* ab da keine Datei mehr gebr. */;

  printf ("Wahrscheinlichkeitsvektoren aufbauen\n");
  char prob_vector1[2][9025]; char prob_vector2[2][9025];
  for (i = 0; i < 9025; ++i)
  {
    j = grab(bin_vector1, i);
    if (j != 31337)
    {
      prob_vector1[0][pv1_len] = j / 95; prob_vector1[1][pv1_len] = j % 95;
      ++pv1_len;
    }
  }
  for (i = 0; i < 9025; ++i)
  {
    j = grab(bin_vector2, i);
    if (j != 31337)
    {
      prob_vector2[0][pv2_len] = j / 95; prob_vector2[1][pv2_len] = j % 95;
      ++pv2_len;
    }
  }

  printf ("%d Restmoeglichkeiten untersuchen\n", pv1_len * pv2_len);
  plain[4] = 0;
  for (i = 0; i < pv1_len; ++i)
  {
    for (j=0; j < pv2_len; ++j)
    {
      plain[0] = prob_vector1[0][i] + 32;
      plain[1] = prob_vector1[1][i] + 32;
      plain[2] = prob_vector2[0][j] + 32;
      plain[3] = prob_vector2[1][j] + 32;
      if (strcmp(crypt(plain, salt), pass) == 0)
      {
        printf ("Pwort: %s\n", plain); return 0; 
      }
    }
  }
  printf ("Pwort wurde nicht zum Salt gefunden\n"); return 1;
}

