// ppm_1.c: Generatorprogramm, nach Vorlage von Jon Erickson
// stellt eine Ausgabedatei bereit.
// g++ -o ppm_1 -l crypt ppm_1.c
// Generierung Testwerte unter UNIX:
// perl -e '$hash = crypt("test", "je"); print "$hash\n";'

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <crypt.h>
#include <sys/types.h>
#include <time.h>

#define HEIGHT 16384  // 64^2*4, X-Achse
#define WIDTH 1129   // 95^2/8, Y-Achse
#define DEPTH 8   // 4 Klartextzeichen  *2, Z-Achse
#define SIZE HEIGHT * WIDTH * DEPTH
#define 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));
}

main()
{
  printf ("Start des Generierungsprogramms ...\n");

  char * plain, * code, * data;
  data = (char *) malloc(SIZE + 19);
  if (! data)
  {
    printf("Fehler: Zu wenig Arbeitsspeicher.\n"); exit(1);
  }
  plain = data+SIZE; code = plain+5;
  printf ("Dateigroesse wird %d Megabyte ...\n", ((int) SIZE >> 20));

  int i, j, k, l;
  unsigned int charval1, charval2, val;
  short int schl, bas;

  time_t start = time(NULL);
  plain[4] = 0;
  for(i=32;i < 127; ++i) // erstes Zeichen
  {
    // Fortschrittsanzeige
    if (i > 32 && ((i & 1) == 0))
    {
      time_t bisherZ = time(NULL) - start;
      time_t gesamtZ = (time_t) ((bisherZ * 95) / (i - 32));
      printf ("Restdauer: %d sek.\n", (gesamtZ - bisherZ) ); 
    }
    plain[0] = (char) i;
    for (j=32;j < 127; ++j) // zweites Zeichen
    {
      plain[1] = (char) j; charval1 = (i-32)*95 + (j-32);
      for (k=32; k < 127;++k) // drittes Zeichen
      {
        plain[2] = (char) k;
        for (l=32; l < 127; ++l) // viertes Zeichen
        {
          plain[3] = (char) l; charval2 = (k-32)*95 + (l-32);
          code = crypt(plain, SALT);  // hier Verschl., hoher Zeitverbrauch
          // Anm.: code[0] und [1] entsprechen dem Salt-Wert, haben also keine
          // Information

          // im Orginalbuch wird hier noch 4 mal der strukturgleiche Block 
          // gemacht; fuegt bei 2|3|4 an, weil an 0|1 der Salt ist.
          for (schl = 0, bas = 2; schl < 4; ++schl, bas += 2)
          {
            val = (HEIGHT * schl) + tripleval(
               code[bas],code[bas+1],code[bas+2]);
            data[(val * WIDTH)+(charval1 >> 3)] |= (1<<(charval1 & 7));
            val += (HEIGHT << 2);
            data[(val * WIDTH)+(charval2 >> 3)] |= (1<<(charval2 & 7));
          }
        }
      }
    }
  }
  printf ("beendet .. speichern ..\n");

  // hier erst Datei oeffnen,weil das oben lange dauern kann
  FILE * handle;
  if (!(handle = fopen("4char.ppm", "w")))
  {
    printf("Error: Coudn't open file '4char.ppm' for writing.\n");
    exit(1);
  }

  fwrite(data,SIZE,1,handle);
  free(data);
  fclose(handle);
} // Ende main
