
#include "RobboLito_def.h"
#include "RobboTotalBase.h"
#include <string.h>

#define IsUVWXZ(x) (((x) == 'U') || ((x) == 'V') || ((x) == 'W') || \
                    ((x) == 'X') || ((x) == 'Z'))
static int canonico_windows (char *AB, boolean * simm)
{
  char Sort[32] = "KQRBNPEFGHIJZUVWX";
  char A[32];
  int j, n, n2, i, SO[32];
  strcpy(A, AB);
  A[16] = 0;
  for (i = 0; i < 16; i++)
    if (A[i] == '0')
      A[i] = 0;
  for (n = 0; n < 16; n++)
    if (!A[n]) break;
  for (i = n; i < 16; i++)
    A[i] = 0;
  for (i = 0; i < 16; i++)
    if (A[i] >= 'a')
      A[i] -= 32;
  for (i = 0; i < 16; i++)
    if (A[i] == 0)
      for (j = i; j < 4; j++)
	A[j] = 0;
  for (i = 0; i < 16; i++)
    if (!strchr (Sort, A[i]))
      A[i] = 0;
  for (n = 1; A[n] != 'K'; n++);
  for (i = 0; i < n; i++)
    for (j = 0; j < strlen (Sort); j++)
      if (Sort[j] == A[i])
	SO[i] = j;
  for (i = n - 1; i >= 1; i--)
    for (j = i + 1; j < n && SO[j - 1] > SO[j]; j++)
      {
	SCAMB (A[j - 1], A[j]);
	SCAMB (SO[j - 1], SO[j]);
      }
  for (n2 = n + 1; A[n2] ; n2++);
  for (i = n; i < n2; i++)
    for (j = 0; j < strlen (Sort); j++)
      if (Sort[j] == A[i])
	SO[i] = j;
  for (i = n + 1; i < n2; i++)
    for (j = 0; j < strlen (Sort); j++)
      if (Sort[j] == A[i])
	SO[i] = j;
  for (i = n2 - 1; i >= n + 1; i--)
    for (j = i + 1; j < n2 && SO[j - 1] > SO[j]; j++)
      {
	SCAMB (A[j - 1], A[j]);
	SCAMB (SO[j - 1], SO[j]);
      }

  if (n == 0)
    *simm = TRUE;
  if (n == 1 && IsUVWXZ(Sort[SO[0]]))
    *simm = TRUE;
  if (n == 2 && Sort[SO[0]] == (Sort[SO[1]] ^ 32))
    *simm = TRUE;
  if (n == 2 && Sort[SO[0]] == 'Z' && Sort[SO[1]] == 'Z')
    *simm = TRUE;
  if (n == 3 && IsUVWXZ(Sort[SO[1]]) &&
      ((Sort[SO[0]] == 'Z' && Sort[SO[2]] == 'Z')
       || Sort[SO[0]] == (Sort[SO[2]] ^ 32)))
    *simm = TRUE;
  if (n == 4 && Sort[SO[1]] == 'Z' && Sort[SO[2]] == 'Z' &&
      ((Sort[SO[0]] == 'Z' && Sort[SO[3]] == 'Z')
       || Sort[SO[0]] == (Sort[SO[3]] ^ 32)))
    *simm = TRUE;
  if (n == 4 && (Sort[SO[0]] == (Sort[SO[2]] ^ 32))
      && (Sort[SO[1]] == (Sort[SO[3]] ^ 32)))
    *simm = TRUE;

  if (n2 == 2)
    *simm = TRUE;
  if (n2 == 3 && IsUVWXZ(Sort[SO[1]]))
    *simm = TRUE;
  if (n2 == 4 && Sort[SO[1]] == Sort[SO[3]])
    *simm = TRUE;
  if (n2 == 4 && Sort[SO[1]] == 'Z' && Sort[SO[2]] == 'Z')
    *simm = TRUE;
  if (n2 == 5 && IsUVWXZ(Sort[SO[2]]) &&
      ((Sort[SO[1]] == 'Z' && Sort[SO[3]] == 'Z')
       || Sort[SO[1]] == Sort[SO[4]]))
    *simm = TRUE;
  if (n2 == 6 && Sort[SO[2]] == 'Z' && Sort[SO[3]] == 'Z' &&
      ((Sort[SO[1]] == 'Z' && Sort[SO[4]] == 'Z')
       || Sort[SO[1]] == Sort[SO[5]]))
    *simm = TRUE;
  if (n2 == 6 && Sort[SO[1]] == Sort[SO[4]] && Sort[SO[2]] == Sort[SO[5]])
    *simm = TRUE; /* Sort nonoperative for Windows */
  return n;
}

int nome_canonico (char *A, boolean * simm)
{
  char Sort[32] = "QRBNPEFGHIJZUVWXqrbnpefghij";
  int j, n, i, SO[MAX_NUM];
  if (A[0] == 'K' || A[0]=='k')
    return canonico_windows (A, simm);
  A[MAX_NUM] = 0;
  for (i = 0; i < MAX_NUM; i++)
    if (A[i] == '0')
      A[i] = 0;
  for (i = 0; i < MAX_NUM; i++)
    if (A[i] == 0)
      for (j = i; j < 4; j++)
	A[j] = 0;
  for (i = 0; i < MAX_NUM; i++)
    if (!strchr (Sort, A[i]))
      A[i] = 0;
  for (n = 0; A[n]; n++);
  for (i = 0; i < n; i++)
    for (j = 0; j < strlen (Sort); j++)
      if (Sort[j] == A[i])
	SO[i] = j;
  for (i = n - 1; i >= 0; i--)
    for (j = i + 1; j < n && SO[j - 1] > SO[j]; j++)
      {
	SCAMB (A[j - 1], A[j]);
	SCAMB (SO[j - 1], SO[j]);
      }
  if (n == 0)
    *simm = TRUE;
  if (n == 1 && IsUVWXZ(Sort[SO[0]]))
    *simm = TRUE;
  if (n == 2 && Sort[SO[0]] == (Sort[SO[1]] ^ 32))
    *simm = TRUE;
  if (n == 2 && Sort[SO[0]] == 'Z' && Sort[SO[1]] == 'Z')
    *simm = TRUE;
  if (n == 3 && IsUVWXZ(Sort[SO[1]]) &&
      ((Sort[SO[0]] == 'Z' && Sort[SO[2]] == 'Z')
       || Sort[SO[0]] == (Sort[SO[2]] ^ 32)))
    *simm = TRUE;
  if (n == 4 && Sort[SO[1]] == 'Z' && Sort[SO[2]] == 'Z' &&
      ((Sort[SO[0]] == 'Z' && Sort[SO[3]] == 'Z')
       || Sort[SO[0]] == (Sort[SO[3]] ^ 32)))
    *simm = TRUE;
  if (n == 4 && (Sort[SO[0]] == (Sort[SO[2]] ^ 32))
      && (Sort[SO[1]] == (Sort[SO[3]] ^ 32)))
    *simm = TRUE;
  return n;
}

int RobboTotal_scop (char* NOME)
{
  RobboTotalBase* tb;
  int i, p[4] = { 0, 0, 0, 0 }, v, EFGH = -1, IJ1 = -1, IJ2 = -1, file, file1, file2;
  for (i = 0; i < 4 && NOME[i]; i++)
    {
      switch (NOME[i])
	{
	case 'Q':
	  p[i] = wEnumQ;
	  break;
	case 'q':
	  p[i] = bEnumQ;
	  break;
	case 'R':
	  p[i] = wEnumR;
	  break;
	case 'r':
	  p[i] = bEnumR;
	  break;
	case 'B':
	  p[i] = wEnumB;
	  break;
	case 'b':
	  p[i] = bEnumB;
	  break;
	case 'N':
	  p[i] = wEnumN;
	  break;
	case 'n':
	  p[i] = bEnumN;
	  break;
	case 'I': case 'J':
	  if (IJ1 == -1)
	    IJ1 = i;
	  else
	    IJ2 = i;
	  p[i] = wEnumP;
	  break;
	case 'E': case 'F': case 'G': case 'H':
	  EFGH = i;
	case 'P':
	  p[i] = wEnumP;
	  break;
	case 'i': case 'j':
	  if (IJ1 == -1)
	    IJ1 = i;
	  else
	    IJ2 = i;
	  p[i] = bEnumP;
	  break;
	case 'e': case 'f': case 'g': case 'h':
	  EFGH = i;
	case 'p':
	  p[i] = bEnumP;
	  break;
	case 'U': case 'V': case 'W': case 'X':
	  EFGH = i;
	case 'Z':
	  p[i] = BlockedPawn;
	  break;
	default:
	  p[i] = 0;
	}
      switch (NOME[i])
	{
	  case 'E': case 'e': case 'U':
	    file = 3;
	    break;
	  case 'F': case 'f': case 'V':
	    file = 2;
	    break;
	  case 'G': case 'g': case 'W':
	    file = 1;
	    break;
	  case 'H': case 'h': case 'X':
	    file = 0;
	    break;
	}
      switch (NOME[i])
	{
	  case 'I':
	    file1 = 0;
	    break;
	  case 'J':
	    file1 = 1;
	    break;
	  case 'i':
	    file2 = 0;
	    break;
	  case 'j':
	    file2 = 1;
	    break;
	}
    }
  v = TotalBaseTable[p[0]][p[1]][p[2]][p[3]];
  if (v == -1)
    return -1;
  v &= 0xffff;
  tb = TABLE_TOTAL_BASES + v;
  if (EFGH != -1)
    v = tb->efgh[file];
  if (IJ1 != -1)
    v = tb->efgh[file1 + 2 * file2];
  return v;
}

void RobboTotal_sott (char *NOME) /* close all EFGHij files, for the bad */
{
  int u, i, p[4] = { 0, 0, 0, 0 }, v;
  int i0, i1, i2, i3, file, file1, file2;
  RobboTotalBase* tb;
  for (i = 0; i < 4 && NOME[i]; i++)
    {
      switch (NOME[i])
	{
	case 'Q':
	  p[i] = wEnumQ;
	  break;
	case 'q':
	  p[i] = bEnumQ;
	  break;
	case 'R':
	  p[i] = wEnumR;
	  break;
	case 'r':
	  p[i] = bEnumR;
	  break;
	case 'B':
	  p[i] = wEnumB;
	  break;
	case 'b':
	  p[i] = bEnumB;
	  break;
	case 'N':
	  p[i] = wEnumN;
	  break;
	case 'n':
	  p[i] = bEnumN;
	  break;
	case 'I': case 'J': case 'E': case 'F': case 'G': case 'H': case 'P':
	  p[i] = wEnumP;
	  break;
	case 'i': case 'j': case 'e': case 'f': case 'g': case 'h': case 'p':
	  p[i] = bEnumP;
	  break;
	case 'U': case 'V': case 'W': case 'X': case 'Z':
	  p[i] = BlockedPawn;
	  break;
	default:
	  p[i] = 0;
	}
      switch (NOME[i])
	{
	  case 'E': case 'e': case 'U':
	    file = 3;
	    break;
	  case 'F': case 'f': case 'V':
	    file = 2;
	    break;
	  case 'G': case 'g': case 'W':
	    file = 1;
	    break;
	  case 'H': case 'h': case 'X':
	    file = 0;
	    break;
	}
      switch (NOME[i])
	{
	  case 'I':
	    file1 = 0;
	    break;
	  case 'J':
	    file1 = 1;
	    break;
	  case 'i':
	    file2 = 0;
	    break;
	  case 'j':
	    file2 = 1;
	}
    }
  v = TotalBaseTable[p[0]][p[1]][p[2]][p[3]];
  if (v == -1)
    return;
  v &= 0xffff;
  tb = TABLE_TOTAL_BASES + v;
  if (tb->indici)
    free (tb->indici);
  if (tb->efgh1 != -1)
    {
      for (u = 0; u < 4; u++)
	if (tb->efgh[u] != 0xffff && (TABLE_TOTAL_BASES + tb->efgh[u])->Fdata)
          {
            fclose ((TABLE_TOTAL_BASES + tb->efgh[u])->Fdata);
            (TABLE_TOTAL_BASES + tb->efgh[u])->Fdata = NULL;
          }
    }
  else if (tb->Fdata)
    {
      fclose (tb->Fdata);
      tb->Fdata = NULL;
    }
#define RTBx(a, b, c, d) TotalBaseTable[p[a]][p[b]][p[c]][p[d]] = -1;
  for (i0 = 0; i0 < 4; i0++)
    for (i1 = 0; i1 < 4; i1++)
      {
	if (i0 == i1)
	  continue;
	for (i2 = 0; i2 < 4; i2++)
	  {
	    if (i2 == i1 || i2 == i0)
	      continue;
	    for (i3 = 0; i3 < 4; i3++)
	      {
		if (i3 == i2 || i3 == i1 || i3 == i0)
		  continue;
		RTBx (i0, i1, i2, i3);
	      }
	  }
      }
}

void un_windows (char* NOME) /* make real name */
{
  int c = 1, w = 0;
  while (NOME[c] != 'k' && NOME[c] != 'K')
    {
      NOME[w] = NOME[c++];
      if (NOME[w] > 96)
	NOME[w] -= 32;
      w++;
    }
  c++;
  while (NOME[c])
    {
      NOME[w] = NOME[c++];
      if (NOME[w] < 96)
	NOME[w] += 32;
      w++;
    }
  for ( ; w <= 4 ; w++)
    NOME[w] = 0;
}

void hack_windows_total (char* NOME) /* comply worthless name */
{
  char TEMP[16];
  int i, j = 0, c = 1;
  strcpy (TEMP, NOME);
  TEMP[0] = 'K';
  while (NOME[j] >= 'A' && NOME[j] <= 'Z')
    TEMP[c++] = NOME[j++];
  TEMP[c++] = 'K';
  while (NOME[j])
    TEMP[c++] = NOME[j++] - 32;
  TEMP[c] = 0;
  strcpy (NOME, TEMP);
}

void get_name (char* output, char* input)
{
  strcpy (output, input);
  if (output[0] == 'K' || output[0] == 'k')
    un_windows (output);
#ifdef NOME_WINDOWS /* simple ? */
  hack_windows_total (output);
#endif
  
}

int RobboHasTotalBase (Type_PiSq* PiSq)
{
  int v;
  if (PiSq->wtm)
    v = TotalBaseTable[PiSq->pi[0]][PiSq->pi[1]][PiSq->pi[2]][PiSq->pi[3]];
  else
    v = TotalBaseTable[col_scamb[PiSq->pi[0]]][col_scamb[PiSq->pi[1]]]
                      [col_scamb[PiSq->pi[2]]][col_scamb[PiSq->pi[3]]];
  if ((v & 0xffff) == 0xffff)
    return FALSE;
  return TRUE;
}

void RobboTotal_add (RobboTotalBase* tb, char* nome, char* DIRECTORY)
{
  int i, j, p[4], v, w, u;
  char NOME2[16], NOME[16];
  boolean PawnFlag = FALSE;
  int c = 1, file;
  for (i = 0; i < 16; i++)
    NOME[i] = NOME2[i] = 0; /* Bug */
  strcpy (NOME, nome);
  if (NOME[0] == 'K' || NOME[0] == 'k')
    un_windows (NOME);
  if (NOME[0] == '.')
    return;
  v = RobboTotal_scop (NOME);
  if (v != -1 && v != 0xffff)
    return;
  strcpy (NOME2, NOME);
#ifdef NOME_WINDOWS /* simple ? */
  hack_windows_total (NOME2);
#endif
  if (TOTAL_VERBOSO)
    SEND ("info string Register RobboTotalBase #%d %s\n", NUM_TOTAL_BASES, NOME2);
  sprintf (tb->DIR_NOME, "%s/", DIRECTORY);
  strcpy (tb->string, NOME2);
  strcpy (tb->canonical_name, NOME);
  tb->efgh1 = tb->efgh2 = -1;
  for (i = 0; i < 4; i++)
    {
      tb->efgh_shift[i] = 0;
      tb->efgh_file[i] = -1;
      tb->efgh[i] = 0xffff;
      switch (NOME[i])
	{
	case 'Q':
	  p[i] = wEnumQ;
	  break;
	case 'q':
	  p[i] = bEnumQ;
	  break;
	case 'R':
	  p[i] = wEnumR;
	  break;
	case 'r':
	  p[i] = bEnumR;
	  break;
	case 'B':
	  p[i] = wEnumB;
	  break;
	case 'b':
	  p[i] = bEnumB;
	  break;
	case 'N':
	  p[i] = wEnumN;
	  break;
	case 'n':
	  p[i] = bEnumN;
	  break;
	case 'I': case 'J':
	  tb->efgh1 = i;
	  p[i] = wEnumP;
	  break;
	case 'E': case 'F': case 'G': case 'H':
	  tb->efgh1 = i;
	case 'P':
	  p[i] = wEnumP;
	  break;
	case 'i': case 'j':
	  tb->efgh2 = i; /* HACK: anterior efgh1 */
	  p[i] = bEnumP;
	  break;
	case 'e': case 'f': case 'g': case 'h':
	  tb->efgh1 = i;
	case 'p':
	  p[i] = bEnumP;
	  break;
	case 'U': case 'V': case 'W': case 'X':
	  tb->efgh1 = i;
	case 'Z':
	  p[i] = BlockedPawn;
	  break;
	default:
	  p[i] = 0;
	}
      switch (NOME[i])
	{
	  case 'E': case 'e': case 'U':
	    file = 3;
	    break;
	  case 'F': case 'f': case 'V':
	    file = 2;
	    break;
	  case 'G': case 'g': case 'W': case 'J': case 'j':
	    file = 1;
	    break;
  	  case 'H': case 'h': case 'X':	case 'I': case 'i':
	    file = 0;
	}
      if (p[i])
	{
	  if (p[i] == wEnumP || p[i] == bEnumP)
	    tb->m[i] = 48;
	  else if (p[i] == BlockedPawn)
	    tb->m[i] = 40;
	  else
	    tb->m[i] = 64;
	  if (tb->efgh2 == i) /* HACK: anterior efgh1 */
	    {
	      tb->m[i] >>= 1;
	      tb->efgh_shift[i] = 1;
	      tb->efgh_file[i] = file;
	      tb->m[tb->efgh1] <<= 1;
	      tb->efgh_shift[tb->efgh1] = 1;
	      file = 2 * file + tb->efgh_file[tb->efgh1];
	    }
	  else if (tb->efgh1 == i)
	    {
	      tb->m[i] >>= 2;
	      tb->efgh_shift[i] = 2;
	      tb->efgh_file[i] = file;
	    }
	}
      else
	tb->m[i] = 1;
    }
  if (p[0] && p[0] == p[3])
    {
      tb->m[0] =(tb->m[0] * (tb->m[0] - 1) *
		 (tb->m[0] - 2) * (tb->m[0] - 3)) / 24;
      tb->m[1] = tb->m[2] = tb->m[3] = 1;
    }
  else if (p[0] && p[0] == p[2])
    {
      tb->m[0] = (tb->m[0] * (tb->m[0] - 1) * (tb->m[0] - 2)) / 6;
      tb->m[1] = tb->m[2] = 1;
    }
  else if (p[1] && p[1] == p[3])
    {
      tb->m[1] = (tb->m[1] * (tb->m[1] - 1) * (tb->m[1] - 2)) / 6;
      tb->m[2] = tb->m[3] = 1;
    }
  else
    {
      if (p[0] && p[0] == p[1])
	{
	  tb->m[0] = (tb->m[0] * (tb->m[0] - 1)) / 2;
	  tb->m[1] = 1;
	}
      if (p[1] && p[1] == p[2])
	{
	  tb->m[1] = (tb->m[1] * (tb->m[1] - 1)) / 2;
	  tb->m[2] = 1;
	}
      if (p[2] && p[2] == p[3])
	{
	  tb->m[2] = (tb->m[2] * (tb->m[2] - 1)) / 2;
	  tb->m[3] = 1;
	}
    }
  for (i = 0; i < 4; i++)
    {
      tb->p[i] = p[i];
      if (p[i] == wEnumP || p[i] == bEnumP || p[i] == BlockedPawn)
	PawnFlag = TRUE;
    }

  tb->num = NUM_TOTAL_BASES;
  tb->PEDONE = PawnFlag;
  tb->pro = 1;
  for (i = 0; i < 4; i++)
    tb->pro *= tb->m[i];
  tb->PawnMult = tb->pro;
  if (!PawnFlag)
    tb->pro *= 462;
  else
    tb->pro *= 1806;
  
  if (TotalBaseTable[p[0]][p[1]][p[2]][p[3]] != -1)
    goto EFGH_ij_FIX;
  TotalBaseTable[p[0]][p[1]][p[2]][p[3]] =
    NUM_TOTAL_BASES + (0 << 16) + (1 << 18) + (2 << 20) + (3 << 22);
  TotalBaseTable[p[1]][p[0]][p[2]][p[3]] =
    NUM_TOTAL_BASES + (1 << 16) + (0 << 18) + (2 << 20) + (3 << 22);
  TotalBaseTable[p[0]][p[2]][p[1]][p[3]] =
    NUM_TOTAL_BASES + (0 << 16) + (2 << 18) + (1 << 20) + (3 << 22);
  TotalBaseTable[p[1]][p[2]][p[0]][p[3]] =
    NUM_TOTAL_BASES + (2 << 16) + (0 << 18) + (1 << 20) + (3 << 22);
  TotalBaseTable[p[2]][p[0]][p[1]][p[3]] =
    NUM_TOTAL_BASES + (1 << 16) + (2 << 18) + (0 << 20) + (3 << 22);
  TotalBaseTable[p[2]][p[1]][p[0]][p[3]] =
    NUM_TOTAL_BASES + (2 << 16) + (1 << 18) + (0 << 20) + (3 << 22);

  TotalBaseTable[p[0]][p[1]][p[3]][p[2]] =
    NUM_TOTAL_BASES + (0 << 16) + (1 << 18) + (3 << 20) + (2 << 22);
  TotalBaseTable[p[0]][p[2]][p[3]][p[1]] =
    NUM_TOTAL_BASES + (0 << 16) + (3 << 18) + (1 << 20) + (2 << 22);
  TotalBaseTable[p[1]][p[0]][p[3]][p[2]] =
    NUM_TOTAL_BASES + (1 << 16) + (0 << 18) + (3 << 20) + (2 << 22);
  TotalBaseTable[p[1]][p[2]][p[3]][p[0]] =
    NUM_TOTAL_BASES + (3 << 16) + (0 << 18) + (1 << 20) + (2 << 22);
  TotalBaseTable[p[2]][p[0]][p[3]][p[1]] =
    NUM_TOTAL_BASES + (1 << 16) + (3 << 18) + (0 << 20) + (2 << 22);
  TotalBaseTable[p[2]][p[1]][p[3]][p[0]] =
    NUM_TOTAL_BASES + (3 << 16) + (1 << 18) + (0 << 20) + (2 << 22);

  TotalBaseTable[p[0]][p[3]][p[1]][p[2]] =
    NUM_TOTAL_BASES + (0 << 16) + (2 << 18) + (3 << 20) + (1 << 22);
  TotalBaseTable[p[0]][p[3]][p[2]][p[1]] =
    NUM_TOTAL_BASES + (0 << 16) + (3 << 18) + (2 << 20) + (1 << 22);
  TotalBaseTable[p[1]][p[3]][p[0]][p[2]] =
    NUM_TOTAL_BASES + (2 << 16) + (0 << 18) + (3 << 20) + (1 << 22);
  TotalBaseTable[p[1]][p[3]][p[2]][p[0]] =
    NUM_TOTAL_BASES + (3 << 16) + (0 << 18) + (2 << 20) + (1 << 22);
  TotalBaseTable[p[2]][p[3]][p[0]][p[1]] =
    NUM_TOTAL_BASES + (2 << 16) + (3 << 18) + (0 << 20) + (1 << 22);
  TotalBaseTable[p[2]][p[3]][p[1]][p[0]] =
    NUM_TOTAL_BASES + (3 << 16) + (2 << 18) + (0 << 20) + (1 << 22);

  TotalBaseTable[p[3]][p[0]][p[1]][p[2]] =
    NUM_TOTAL_BASES + (1 << 16) + (2 << 18) + (3 << 20) + (0 << 22);
  TotalBaseTable[p[3]][p[0]][p[2]][p[1]] =
    NUM_TOTAL_BASES + (1 << 16) + (3 << 18) + (2 << 20) + (0 << 22);
  TotalBaseTable[p[3]][p[1]][p[0]][p[2]] =
    NUM_TOTAL_BASES + (2 << 16) + (1 << 18) + (3 << 20) + (0 << 22);
  TotalBaseTable[p[3]][p[1]][p[2]][p[0]] =
    NUM_TOTAL_BASES + (3 << 16) + (1 << 18) + (2 << 20) + (0 << 22);
  TotalBaseTable[p[3]][p[2]][p[0]][p[1]] =
    NUM_TOTAL_BASES + (2 << 16) + (3 << 18) + (1 << 20) + (0 << 22);
  TotalBaseTable[p[3]][p[2]][p[1]][p[0]] =
    NUM_TOTAL_BASES + (3 << 16) + (2 << 18) + (1 << 20) + (0 << 22);
  if (tb->efgh1 != -1)
    tb->efgh[file] = NUM_TOTAL_BASES;
  NUM_TOTAL_BASES++;
  return;
 EFGH_ij_FIX:
  v = TotalBaseTable[p[0]][p[1]][p[2]][p[3]] & 0xffff;  
  for (u = 0; u < 4; u++)
    if (((w = (TABLE_TOTAL_BASES + v)->efgh[u])) != 0xffff)
      {
	(TABLE_TOTAL_BASES + w)->efgh[file] = NUM_TOTAL_BASES;
	tb->efgh[u] = w;
      }
  tb->efgh[file] = NUM_TOTAL_BASES;
  NUM_TOTAL_BASES++;
}

/********************************/ /********************************/

int STAT (char *filename); /* non funzione Windows */
boolean RobboTotal_registro (char* nome, char *DIRECTORY)
{
  int i, j, v;
  RobboTotalBase* tb;
  char NOME[64], NOME_FILE[1024], STRING[16];
  FILE* F;
  uint8 A[4];
  uint32 n = 0;
  int num_indici = 0;
  int e = 0;
  strcpy (NOME, nome);
  if (NOME[0] == 'K' || NOME[0] == 'k')
    un_windows (NOME);
  for (i = 0; i < 4; i++)
    if (NOME[i] == '0')
      NOME[i] = 0;
  for (i = 0; i < 4; i++)
    if (NOME[i] == 0)
      for (j = i; j < 4; j++)
	NOME[j] = 0;
  NOME[4] = 0;
  v = RobboTotal_scop (NOME);
  if (v != -1 && v != 0xffff)
    return FALSE;
  get_name (STRING, NOME);
  sprintf (NOME_FILE, "%s/%s%s", DIRECTORY, COMPRESSA_PREFISSO + 1, STRING);
  if (STAT (NOME_FILE) == -1)
    {
      if (STRING[0] == 'K')
	un_windows (STRING);
      else /* HACK */
	hack_windows_total (STRING);
      sprintf (NOME_FILE, "%s/%s%s", DIRECTORY, COMPRESSA_PREFISSO + 1, STRING);      
      if (STAT (NOME_FILE) == -1)
	{
	  SEND ("STAT failed %s\n", NOME_FILE);
	  return FALSE;
	}
    }
  sprintf (NOME_FILE, "%s/%s%s", DIRECTORY, COMPRESSA_PREFISSO, STRING);
  F = fopen (NOME_FILE, "rb");
  if (!F)
    {
      SEND ("FOPEN failed %s\n", NOME_FILE);
      return FALSE;
    }
  fread (A, 1, 4, F);
  /* Header 0xca 0x7f 0x0b 0xaa */
  if (A[0] != 0xca || A[1] != 0x7f || A[2] != 0x0b || A[3] != 0xaa)
    {
      SEND ("iCOMP bytes failed %s\n", NOME_FILE);
      fclose (F);
      return FALSE;
    }
  tb = TABLE_TOTAL_BASES + NUM_TOTAL_BASES;
  RobboTotal_add (tb, NOME, DIRECTORY);
  tb->TYPE = DISCO_COMPRESSO;
  tb->Fdata = NULL;  
  strcpy (tb->string, STRING); /* HACK */
  /* Header, block size and compression type */
  fread (A, 1, 4, F);
  for (i = 0; i < 4; i++)
    n |= A[i] << (8 * i);
  tb->BLOCCO_pro = n & ((1 << 28) - 1);
  tb->BWT = n >> 28;
  fread (A, 1, 4, F); /* Header, null 4 bytes */
  fread (A, 1, 4, F); /* Header, number of indices */
  for (i = 0; i < 4; i++)
    num_indici |= A[i] << (8 * i);
  tb->indici = malloc (4 * num_indici);
  fread (tb->indici, 4, num_indici, F); /* ENDIAN */
  TOT_INDICI += num_indici;
  fclose (F);
  /* above 32-bit with index */
  for (i = 0; i < 8; i++)
    tb->supp_indice[i] = 0xffffffff;
  for (i = 1; i <= num_indici - 1; i++)
    if (tb->indici[i] < tb->indici[i - 1])
      tb->supp_indice[e++] = i;
  tb->num_indici = num_indici;
  return TRUE;
}

/********************************/ /********************************/

#ifdef WINDOWS
#include <sys/types.h>
#include <sys/stat.h>
int STAT (char *filename) /* intendant "stat" ? */
{
  struct _stat buf[1];
  if (_stat (filename, buf) == -1)
    return -1;
  return (buf->st_mode & _S_IREAD) ? 0 : -1;
}
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int STAT (char *filename)
{
  struct stat buf[1];
  if (stat (filename, buf) == -1)
    return -1;
  return (buf->st_mode & S_IRUSR) ? 0 : -1;
}
#endif

#define WINDOWS_PREFISSO "icomp."
static void add_nome_total (char* NOME, char* DIRECTORY)
{
  int n = strlen (COMPRESSA_PREFISSO);
  if (memcmp (NOME, COMPRESSA_PREFISSO, n) && memcmp (NOME, WINDOWS_PREFISSO, n))
    return;
  RobboTotal_registro (NOME + n, DIRECTORY);
}

static void TotalBaseCountList ()
{
  SEND ("info string Now %d RobboTotalBases with %dmb for indexing\n",
	NUM_TOTAL_BASES, (int) ((TOT_INDICI * 4) >> 20));
}

static void BlockTotalIndexRegister (char* DIR, FILE* F) /* for the TEST? */
{
  char NOME[16], STRING[16];
  uint8 A[4];
  unsigned char B[16];
  RobboTotalBase* tb;
  int i, num_indici, n, v, e;
  boolean SOTT;
  char NOME_FILE[1024];
  NOME[4] = 0;
  while (fread (B, 1, 4, F)) /* 0xee 0x63 0x70 0x41 */
    {
      if (B[0] != 0xee || B[1] != 0x63 || B[2] != 0x70 || B[3] != 0x41)
	{
	  SEND ("BlockIndex mask failure!\n");
	  return;
	}
      fread (NOME, 1, 4, F); /* initials of file */
      v = RobboTotal_scop (NOME);
      if (v != -1 && v != 0xffff) /* capable ? */
	{
	  tb = TABLE_TOTAL_BASES + (v & 0xffff);
	  SEND ("Notably loaded %s\n", tb->string);
	  free (tb->indici);
	  if (tb->Fdata)
	    fclose (tb->Fdata);
	}
      get_name (STRING, NOME);
      sprintf (NOME_FILE, "%s/%s%s", DIR, COMPRESSA_PREFISSO + 1, STRING);
      if (STAT (NOME_FILE) == -1)
	{
	  if (STRING[0] == 'K')
	    un_windows (STRING);
	  else /* HACK */
	    hack_windows_total (STRING); /* worthless name (WINDOWS) */
	  sprintf (NOME_FILE, "%s/%s%s", DIR, COMPRESSA_PREFISSO + 1, STRING);
	  if (STAT (NOME_FILE) == -1)
	    {
	      SEND ("STAT failed %s\n", NOME_FILE);
	      SOTT = TRUE;
	    }
	  else
	    SOTT = FALSE;
	}      
      else
	SOTT = FALSE;
      if (!SOTT)
	{
	  tb = TABLE_TOTAL_BASES + NUM_TOTAL_BASES;
	  RobboTotal_add (tb, NOME, DIR);
	  strcpy (tb->string, STRING); /* HACK */
	  tb->TYPE = DISCO_COMPRESSO;
	  tb->Fdata = NULL;
	}
      fread (A, 1, 4, F); /* block size + compression type */
      for (i = 0; i < 4; i++)
	n |= A[i] << (8 * i);
      tb->BLOCCO_pro = n & ((1 << 28) - 1);
      tb->BWT = n >> 28;
      fread (A, 1, 4, F); /* number of index count */      
      num_indici = 0;
      for (i = 0; i < 4; i++)
	num_indici |= A[i] << (8 * i);
      tb->indici = malloc (4 * num_indici);
      fread (tb->indici, 4, num_indici, F); /* ENDIAN */
      if (!SOTT)
	TOT_INDICI += num_indici;
      for (i = 0; i < 8; i++)
	tb->supp_indice[i] = 0xffffffff;
      e = 0;
      for (i = 1; i <= num_indici - 1; i++)
	if (tb->indici[i] < tb->indici[i - 1])
	  tb->supp_indice[e++] = i;
      tb->num_indici = num_indici;
    }
  TotalBaseCountList ();
}

static boolean HasTotalBlockIndex (char* DIR)
{
  FILE* F;
  char NAME[256];
  sprintf (NAME, "%s/BLOCK.INDEX", DIR);
  F = fopen (NAME, "rb");
  if (!F)
    return FALSE;
  SEND ("Using BLOCK.INDEX for %s\n", DIR);
  BlockTotalIndexRegister (DIR, F);
  fclose (F);
  return TRUE;
}

#ifdef WINDOWS /* HACK */
void GetTotalBase (char* A)
{
  char B[1024];
  WIN32_FIND_DATA ffd;
  HANDLE hFind = INVALID_HANDLE_VALUE;

  if (HasTotalBlockIndex (A))
    return;
  SEND ("info string Reading directory %s\n", A);
  strcpy (B, A);
  strcat (B, "\\*");
  hFind = FindFirstFile (B, &ffd);
  if (hFind == INVALID_HANDLE_VALUE)
    {
      SEND ("info string Directory %s not found\n", A);
      return;
    }
  do
    {
      if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) /* recurse ? */
	add_nome_total (ffd.cFileName, A);
    }
  while (FindNextFile (hFind, &ffd));
  FindClose (hFind);
  TotalBaseCountList ();
}
#else
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
void GetTotalBase (char* A)
{
  DIR *dir;
  struct dirent *DE;
  if (HasTotalBlockIndex (A))
    return;
  SEND ("info string Reading directory %s\n", A);
  dir = opendir (A);
  if (!dir)
    {
      SEND ("info string Directory %s not found\n", A);
      return;
    }
  while ((DE = readdir (dir)))
    add_nome_total (DE->d_name, A);
  closedir (dir);
  TotalBaseCountList ();
}
#endif
