/*

  Read Sokoban levels from files and export in format usable by Stevedore.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
//#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>

#define LINUX 1
#define uint8 unsigned char

#ifndef TRUE
#define TRUE (0==0)
#define FALSE (0!=0)
#endif

#define MAX_OBJECTS 16

static int lowindex, highindex; // command-line parameters to select puzzles from larger set.

#define MAX_LINE 1024

static char Id[MAX_LINE];
static int Width, Height;
static int xoffset, yoffset;
static int grabbing = FALSE;
static int blocks;
int map[18][18];

void floodfill_same(int x, int y, int same_ch, int replacement_ch) {
  if ((x < 0) || (y < 0) || (x > 17) || (y > 17)) return;
  if (map[x][y] == same_ch) map[x][y] = replacement_ch; else return;
  floodfill_same(x-1,y, same_ch, replacement_ch);
  floodfill_same(x+1,y, same_ch, replacement_ch);
  floodfill_same(x,y-1, same_ch, replacement_ch);
  floodfill_same(x,y+1, same_ch, replacement_ch);
}

void floodfill_hack(int x, int y) {
  if ((x < 0) || (y < 0) || (x > 17) || (y > 17)) return;
  if (map[x][y] == ' ') {
    map[x][y] |= 0x80;
    floodfill_hack(x-1,y);
    floodfill_hack(x+1,y);
    floodfill_hack(x,y-1);
    floodfill_hack(x,y+1);
    floodfill_hack(x-1,y-1);
    floodfill_hack(x+1,y+1);
    floodfill_hack(x+1,y-1);
    floodfill_hack(x-1,y+1);
    return; 
  } else if (((x == 0) || (y == 0) || (x == 17) || (y == 17)) && ((map[x][y] == '+') || (map[x][y] == '-') || (map[x][y] == '|'))) { // wall touches boundary
    map[x][y] |= 0x80;
    return;
  } else if (map[x][y] == '+') {
    map[x][y] |= 0x80;
    return;
  } else if (map[x][y] == '-') {
    map[x][y] |= 0x80;
    return;
  } else if (map[x][y] == '|') {
    map[x][y] |= 0x80;
    return;
  } 
}

int hack(void) {
  int count, x, y;

  // completely surround playfield with blocks
  for (x = 0, y =  0; x < 18; x++) floodfill_same(x, y, ' ', '#');
  for (x = 0, y = 17; x < 18; x++) floodfill_same(x, y, ' ', '#');
  for (y = 0, x =  0; y < 18; y++) floodfill_same(x, y, ' ', '#');
  for (y = 0, x = 17; y < 18; y++) floodfill_same(x, y, ' ', '#');

  // replace all surrounding blocks with 'ignore' codes
  for (x = 0, y =  0; x < 18; x++) floodfill_same(x, y, '#', 'X');
  for (x = 0, y = 17; x < 18; x++) floodfill_same(x, y, '#', 'X');
  for (y = 0, x =  0; y < 18; y++) floodfill_same(x, y, '#', 'X');
  for (y = 0, x = 17; y < 18; y++) floodfill_same(x, y, '#', 'X');

  // For the innermost layer only of ignore codes surrounding the playfield,
  // replace with 'wall' code
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if (map[x][y] == 'X' && (((x < 17) && (map[x+1][y] != 'X') && (map[x+1][y] != 'Y') ) ||
			       ((x >  0) && (map[x-1][y] != 'X') && (map[x-1][y] != 'Y')) ||
			       ((y < 17) && (map[x][y+1] != 'X') && (map[x][y+1] != 'Y')) ||
			       ((y >  0) && (map[x][y-1] != 'X') && (map[x][y-1] != 'Y'))) ) {
	map[x][y] = 'Y';
      }
    }
  }

  // convert outside wall from 8-connected to 4-connected topology (ie fill in corners on diagonal wall paths)
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      // XY   ->  ZY
      // Y        Y
      if ((x < 17) && (y < 17) && (map[x][y] == 'X') && (map[x+1][y] == 'Y') && (map[x][y+1] == 'Y') && (map[x+1][y+1] != 'Y')) map[x][y] = 'Z';
      // YX       YZ
      //  Y   ->   Y
      if ((x > 0) && (y < 17) && (map[x][y] == 'X') && (map[x-1][y] == 'Y') && (map[x][y+1] == 'Y') && (map[x-1][y+1] != 'Y')) map[x][y] = 'Z';
      //  Y        Y
      // YX   ->  YZ
      if ((x > 0) && (y > 0) && (map[x][y] == 'X') && (map[x-1][y] == 'Y') && (map[x][y-1] == 'Y') && (map[x-1][y-1] != 'Y')) map[x][y] = 'Z';
      // Y        Y
      // XY   ->  ZY
      if ((x < 17) && (y > 0) && (map[x][y] == 'X') && (map[x+1][y] == 'Y') && (map[x][y-1] == 'Y') && (map[x+1][y-1] != 'Y')) map[x][y] = 'Z';
    }
  }

  // remove ignore codes (spaces look nicer) and mark corners
  // (generation of vector walls much easier if corners are marked)
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      // and finally...
      if (map[x][y] == 'X') map[x][y] = ' ';
      if (map[x][y] == 'Z') map[x][y] = '+';
    }
  }

  // fix up some more corners not previously handled
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      // Y+   ->  ++
      // +        +
      if ((x < 17) && (y < 17) && (map[x][y] == 'Y') && ((map[x+1][y] == 'Y') || (map[x+1][y] == '+')) && ((map[x][y+1] == 'Y') || (map[x][y+1] == '+'))) map[x][y] = '+';
      // +Y       ++
      //  +   ->   +
      if ((x > 0) && (y < 17) && (map[x][y] == 'Y') && ((map[x-1][y] == 'Y') || (map[x-1][y] == '+')) && ((map[x][y+1] == 'Y') || (map[x][y+1] == '+'))) map[x][y] = '+';
      //  +        +
      // +Y   ->  ++
      if ((x > 0) && (y > 0) && (map[x][y] == 'Y') && ((map[x-1][y] == 'Y') || (map[x-1][y] == '+')) && ((map[x][y-1] == 'Y') || (map[x][y-1] == '+'))) map[x][y] = '+';
      // +        +
      // Y+   ->  ++
      if ((x < 17) && (y > 0) && (map[x][y] == 'Y') && ((map[x+1][y] == 'Y') || (map[x+1][y] == '+')) && ((map[x][y-1] == 'Y') || (map[x][y-1] == '+'))) map[x][y] = '+';
    }
  }

  // replace vertical walls with |
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if ((map[x][y] == 'Y') &&
	  (((y >  0) && ((map[x][y-1] == 'Y') || (map[x][y-1] == '+'))) ||
	   ((y < 17) && ((map[x][y+1] == 'Y') || (map[x][y+1] == '+'))))
	 ) map[x][y] = '|';
    }
  }

  // and horizontal walls with -
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if ((map[x][y] == 'Y') &&
	  (((x >  0) && ((map[x-1][y] == 'Y') || (map[x-1][y] == '+'))) ||
	   ((x < 17) && ((map[x+1][y] == 'Y') || (map[x+1][y] == '+'))))
	 ) map[x][y] = '-';
    }
  }

  // Floodfill again to mark outer walls only
  for (x = 0, y =  0; x < 18; x++) floodfill_hack(x,y);
  for (x = 0, y = 17; x < 18; x++) floodfill_hack(x,y);
  for (y = 0, x =  0; y < 18; y++) floodfill_hack(x,y);
  for (y = 0, x = 17; y < 18; y++) floodfill_hack(x,y);

  // replace all internal walls (unmarked) with fixed blocks
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if ((map[x][y] == '+') || (map[x][y] == '-') || (map[x][y] == '|') ) {
	map[x][y] = '#';
      }
    }
  }

  // And restore outer wall by removing marking
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      map[x][y] &= 0x7F;
    }
  }

  // one last set of patching up to do...
  //
  // |      |      |
  // +  or  +  =>  |
  // |      +      |
  //
  for (y = 0; y < 16; y++) {
    for (x = 0; x < 18; x++) {
      if (map[x][y] == '|' && map[x][y+1] == '+' &&
	  (map[x][y+2] == '+' || map[x][y+2] == '|')
	  ) map[x][y+1] = '|';

          if (map[x][y] == '+' && map[x][y+1] == '+' &&
	  (map[x][y+2] == '+' || map[x][y+2] == '|')
	  ) map[x][y+1] = '|';
}
  }
  // and similarly for horizontal...
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 16; x++) {
      if (map[x][y] == '-' && map[x+1][y] == '+' &&
	  (map[x+2][y] == '+' || map[x+2][y] == '-')
	  ) map[x+1][y] = '-';

          if (map[x][y] == '+' && map[x+1][y] == '+' &&
	  (map[x+2][y] == '+' || map[x+2][y] == '-')
	  ) map[x+1][y] = '-';
}
  }
  
  // WOW.  That was quite a hack.
  count = 0;
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      // count all blocks but not external wall
      if (map[x][y] == '.' || map[x][y] == '$' || map[x][y] == '@' || map[x][y] == '#') count += 1; 
    }
  }

  // return count of blocks that need to be drawn explicitly
  return count;
}

int wall(int x, int y) {
  if ((x < 0) || (y < 0) || (x > 17) || (y > 17)) return FALSE;
  return map[x][y] == '+' || map[x][y] == '|' || map[x][y] == '-' || map[x][y] == '#';
}

int eliminated(void) { // eliminate maps that we don't want to handle
  int x, y; //, neighbours;

  // problematic artefacts
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if (map[x][y] == 'Y') return TRUE;
    }
  }

#ifdef NEVER
  // outer walls which fork off segments inside the maze.  Too much hassle to draw
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if (map[x][y] == '+') {
	neighbours = (wall(x-1,y)?1:0) + (wall(x+1,y)?1:0) + (wall(x,y-1)?1:0) + (wall(x,y+1)?1:0);
	if (neighbours > 2) return TRUE;
      }
    }
  }
#endif
  
  // otherwise all good
  return FALSE;
}

static int gameno = 0;

void output_title(char *s) {
  int chars = 0;
  while (*s == ' ') s++;
  while (*s != '\0') {
    if (isalpha(*s)) {
      if (islower(*s)) {
	putchar(toupper(*s)); chars++;
      } else {
        putchar(*s); chars++;
      }
      s += 1;
    } else if (*s == ' ') {
      putchar(*s); chars++;
      while (*s == ' ') s++;
    } else if (isdigit(*s)) {
      putchar(*s); chars++;
      s += 1;
    } else s += 1;
  }
  while (chars < 4) {
    putchar(' '); chars++;
  }
}

int handle(char *s) {
  static int ypos;
  int x,y;
  char Line[MAX_LINE];
  //fprintf(stderr, "%s",s);
  //    <Level Id="yConnection" Width="9" Height="10">

  // start of a level
  if (sscanf(s, " <Level Id=\"%[^\"]\" Width=\"%d\" Height=\"%d\"> ", Id, &Width, &Height) == 3) {
    //fprintf(stderr, "LEVEL! %s\n",s);
    if ((Width <= 18) && (Height <= 18)) {
      for (y = 0; y < 18; y++) {
        for (x = 0; x < 18; x++) {
  	  map[x][y] = ' ';
        }
      }
      xoffset = (18-Width)/2;
      ypos = yoffset = (18-Height)/2;
      grabbing = TRUE;
    }

  // end of a level
  } else if (grabbing && strstr(s, "</Level>")) {
    //fprintf(stderr, "END LEVEL! %s\n",s);

    // Erode thick wall layers with flood fill algorithm!
    blocks = hack();
    grabbing = FALSE;
   
    if ((blocks > 7) && (blocks <= MAX_OBJECTS) && (!(eliminated()))) { // plausible size to be worth looking at
      if (gameno >= lowindex) {
        fprintf(stdout, "#define title%0d \"", gameno);
	output_title(Id);
        fprintf(stdout, "\"\n");
        fprintf(stdout, "#define game%0d \\\n", gameno);
        for (y = 0; y < 18; y++) {
          putchar('"');
          for (x = 0; x < 18; x++) {
            putchar(map[x][y]);
          }
          putchar('"');
          putchar(' ');
          if (y!=17) putchar('\\');
          putchar('\n');
        }
      }
      return TRUE;

    }

  // row within a level
  } else if (grabbing && (sscanf(s, " <L>%[^<]</L> ", Line) == 1)) {
    int xpos;
    //fprintf(stderr, "LINE! %s\n",s);
    xpos = xoffset;
    s = Line;
    while (*s != '\0') {
      if ((*s == '+') || (*s == '*')) grabbing = FALSE; // not handling these yet.
      map[xpos++][ypos] = *s;
      s += 1;
    }
    ypos += 1;
  }
  return FALSE;
}

#define MAX_VECS 100
int vecs;
int vectors[3*MAX_VECS];

void addvec(int a, int b, int c) {
  int base = vecs*3;
  int half, third;
  if (b >= 14) {
    third = b/3;
    vectors[base++] = a;
    vectors[base++] = third;
    vectors[base++] = c;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = third;
    vectors[base++] = c;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b-third*2;
    vectors[base++] = c;
  } else if (b >= 7) {
    half = b/2;
    vectors[base++] = a;
    vectors[base++] = half;
    vectors[base++] = c;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b-half;
    vectors[base++] = c;
  } else if (b <= -14) {
    third = -b/3;
    vectors[base++] = a;
    vectors[base++] = -third;
    vectors[base++] = c;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = -third;
    vectors[base++] = c;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b+third*2;
    vectors[base++] = c;
  } else if (b <= -7) {
    half = -b/2;
    vectors[base++] = a;
    vectors[base++] = -half;
    vectors[base++] = c;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b+half;
    vectors[base++] = c;
  } else if (c >= 14) {
    third = c/3;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = third;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = third;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = c-third*2;
  } else if (c >= 7) {
    half = c/2;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = half;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = c-half;
  } else if (c <= -14) {
    third = -c/3;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = -third;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = -third;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = c+third*2;
  } else if (c <= -7) {
    half = -c/2;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = -half;
    vecs++;
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = c+half;
  } else {
    vectors[base++] = a;
    vectors[base++] = b;
    vectors[base++] = c;
  }
  vecs++;
}

int startx, starty;
void dumpvecs(void) {
  int i, base;
  // may need to insert extra move_abs if too many vectors in perimeter, and wobble occurs...
  if (startx-8-2 < -6) {
    if (starty-8 < -6) {
      fprintf(stdout, "const int wall%0d[%d] = {\n  %d,\n", gameno, 1+vecs*3+3+3, vecs+1+1);
      fprintf(stdout, "  0,%d*H,%d*W,\n", -4, -4);                     // check on code 0 - is it abs, or rel with a different scale?
      fprintf(stdout, "  0,%d*H,%d*W,\n", starty-8+4, startx-8-2+4);
    } else {
      fprintf(stdout, "const int wall%0d[%d] = {\n  %d,\n", gameno, 1+vecs*3+3+3, vecs+1+1);
      fprintf(stdout, "  0,%d*H,%d*W,\n", starty-8, -4);
      fprintf(stdout, "  0,0,%d*W,\n", startx-8-2+4);
    }
  } else {
    if (starty-8 < -6) {
      fprintf(stdout, "const int wall%0d[%d] = {\n  %d,\n", gameno, 1+vecs*3+3+3, vecs+1+1);
      fprintf(stdout, "  0,%d*H,%d*W,\n", -4, startx-8-2);
      fprintf(stdout, "  0,%d*H,0,\n", starty-8+4);
    } else {
      fprintf(stdout, "const int wall%0d[%d] = {\n  %d,\n", gameno, 1+vecs*3+3, vecs+1);
      fprintf(stdout, "  0,%d*H,%d*W,\n", starty-8, startx-8-2);
    }
  }
  for (i = 0; i < vecs; i++) {
    base = i*3;
    fprintf(stdout, "  %d,%d*H,%d*W,\n", vectors[base], vectors[base+1], vectors[base+2]);
  }
  fprintf(stdout, "};\n\n");
}


void Convert(int x, int y, int lastx, int lasty, int tag) {

  // walk around outline of play area, generating vectors suitable for universal drawlist format
  if (tag == '!') {startx = x; starty = y; vecs = 0;}
  
  map[x][y] = tag; // avoid accidental backtracking
  
  if (map[x+1][y] == '-') {
    assert(lasty == 0);
    Convert(x+1,y, lastx+1,0, 'x');
  } else if ((map[x+1][y] == '+')  || (map[x+1][y] == '!')) {
    //fprintf(stdout, "  -1, 0, W*%d,\n", lastx+1);
    addvec(-1,0,lastx+1);
    Convert(x+1,y, 0,0, 'x');

  } else if (map[x][y+1] == '|') {
    assert(lastx == 0);
    Convert(x,y+1, 0,lasty+1, 'x');
  } else if ((map[x][y+1] == '+') || (map[x][y+1] == '!')) {
    //fprintf(stdout, "  -1, H*%d, 0,\n", lasty+1);
    addvec(-1,lasty+1,0);
    Convert(x,y+1, 0,0, 'x');

  } else if (map[x-1][y] == '-') {
    assert(lasty == 0);
    Convert(x-1,y, lastx-1,0, 'x');
  } else if ((map[x-1][y] == '+') || (map[x-1][y] == '!')) {
    //fprintf(stdout, "  -1, 0, W*%d,\n", lastx-1);
    addvec(-1,0,lastx-1);
    Convert(x-1,y, 0,0, 'x');

  } else if (map[x][y-1] == '|') {
    assert(lastx == 0);
    Convert(x,y-1, 0,lasty-1, 'x');
  } else if ((map[x][y-1] == '+') || (map[x][y-1] == '!')) {
    //fprintf(stdout, "  -1, H*%d, 0,\n", lasty-1);
    addvec(-1,lasty-1,0);
    Convert(x,y-1, 0,0, 'x');
  }

  return; //implicit 'else' is  recursion stopper.
}

void gen_sok(FILE *SOK) {
  int x, y;
  char c;
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      c = map[x][y];
      switch (c) {
      case 'x':
      case '+':
      case '-':
      case '|':
        fputc('#', SOK);
        break;
      default:
        fputc(c, SOK);
        break;
      }
    }
    fputc('\r', SOK); // essential for YASS
    fputc('\n', SOK);
  }
}

void gen_outline(FILE *sokfile) {
  int x, y;
#ifdef NEVER
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if (map[x][y] == '@') {
        if (gameno >= lowindex) fprintf(stdout, "\n// Player start pos is y=%d x=%d\n", y, x);
      }
    }
  }
#endif
  for (y = 0; y < 18; y++) {
    for (x = 0; x < 18; x++) {
      if (map[x][y] == '+') {
	if (gameno >= lowindex) {
          Convert(x,y, 0,0, '!');
          dumpvecs();
          gen_sok(sokfile);
	}
	gameno++;
        return;
      }
    }
  }
}

int main (int argc, char **argv) {
  char *fname, *s, line[MAX_LINE+1];
  int i, num_games;
  FILE *file, *sokfile;

  if (argc == 2) {
    lowindex = 0;
    highindex = 10000;
  } else if (argc == 3) {
    if (!isdigit(*argv[2])) {
      fprintf(stderr, "%s: low index (%s) must be an integer 0 or higher\n", argv[0], argv[2]);
      exit(EXIT_FAILURE);
    }
    lowindex = atoi(argv[2]);
    highindex = lowindex;
  } else if (argc == 4) {
    if (!isdigit(*argv[2])) {
      fprintf(stderr, "%s: low index (%s) must be an integer 0 or higher\n", argv[0], argv[2]);
      exit(EXIT_FAILURE);
    }
    lowindex = atoi(argv[2]);
    if (!isdigit(*argv[3])) {
      fprintf(stderr, "%s: high index (%s) must be an integer 0 or higher\n", argv[0], argv[3]);
      exit(EXIT_FAILURE);
    }
    highindex = atoi(argv[3]);
  } else {
    fprintf (stderr, "syntax: %s levels/file.slc [lowindex] [highindex] > output.h\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  fname = argv[1];
  sokfile = fopen("temp.sok", "wb");
  if (sokfile == NULL) {
    fprintf(stderr, "%s: cannot open temp.sok - %s\n", fname, strerror(errno));
    exit(EXIT_FAILURE);
  }
  
  if (lowindex > highindex) {
    fprintf(stderr, "%s: low index (%d) must not be greater than high index (%d).\n", argv[0], lowindex, highindex);
    exit(EXIT_FAILURE);
  }
  
  file = fopen(fname, "r");
  if (file == NULL) {
    fprintf(stderr, "%s: cannot open %s - %s\n", argv[0], fname, strerror(errno));
    exit(EXIT_FAILURE);
  }

  fprintf(stdout, "#define COLLECTION \"");
  {char *s = fname, *t;
    t = strrchr(fname, '/');
    if (t) s = t+1;
    while (*s != '\0') {
      if (*s == '.') break;
      if (isalpha(*s)) {
        if (isupper(*s)) putchar(*s); else putchar(toupper(*s));
      } else if (isdigit(*s)) {
        putchar(*s);
      } else if (*s == ' ') {
        putchar(' ');
      } else {
        // skip
      }
      s += 1;
    }
  }

  fprintf(stdout, "\"\n");

  for (;;) {
    s = fgets(line, MAX_LINE, file); line[MAX_LINE] = '\0';
    if (s == NULL) break;
    if (handle(line)) {
      gen_outline(sokfile);
    }
    if (gameno > highindex) break;
  }

  // if bounds not known, would be better to output this at end:
  num_games = gameno-lowindex;
  fprintf(stdout, "#define NUM_GAMES %d\n", num_games);

  fprintf(stdout, "const char const *games[NUM_GAMES]  __attribute__ ((section(\".text\"))) = {\n");
  for (i = 0; i < num_games; i++) {
    fprintf(stdout, "  game%0d,\n", i+lowindex);
  }
  fprintf(stdout, "};\n\n");
  
  fprintf(stdout, "const int const *walls[NUM_GAMES]  __attribute__ ((section(\".text\"))) = {\n");
  for (i = 0; i < num_games; i++) {
    fprintf(stdout, "  wall%0d,\n", i+lowindex);
  }
  fprintf(stdout, "};\n\n");

  fprintf(stdout, "const char const *titles[NUM_GAMES]  __attribute__ ((section(\".text\"))) = {\n");
  for (i = 0; i < num_games; i++) {
    fprintf(stdout, "  title%0d,\n", i+lowindex);
  }
  fprintf(stdout, "};\n\n");

  // now handle the generation of solutions...
  fclose(sokfile);
  system("echo | wine yass temp.sok > yass.out 2> yass.err");
  remove("temp.sok");
  remove("temp, YASS 2.142 Solutions, Statistics.txt");
  sokfile = fopen("temp, YASS 2.142 Solutions.sok", "r");
  if (sokfile) {
    int c;
    fprintf(stdout, "const char const *solutions[NUM_GAMES]  __attribute__ ((section(\".text\"))) = {\n");
    for (i = 0; i < num_games; i++) {
      for (;;) {
        s = fgets(line, MAX_LINE, sokfile); line[MAX_LINE] = '\0';
        if (s == NULL) break;
        if (strstr(line, "Solution/Pushes")) break;
      }
      if (s == NULL) break;
      fprintf(stdout, "  \"");
      for (;;) {
        c = fgetc(sokfile);
        if (c == '\r') continue;
        if (c == ' ') continue;
        if (c == '\n') {
          c = fgetc(sokfile);
          if (c == '\r') c = fgetc(sokfile);
          if (c == '\n') break;
          if (c == EOF) break;
        }
        putchar(c);
      }
      fprintf(stdout, "\",\n");
      if (c == EOF) break;
    }
    fprintf(stdout, "};\n\n");
    fclose(sokfile);
    if (i == num_games) remove("temp, YASS 2.142 Solutions.sok");
  }
  exit (EXIT_SUCCESS);
  return EXIT_FAILURE;
}